C ++ - 析构函数是解除内存还是实际删除对象?

时间:2017-08-02 14:05:53

标签: c++ pointers memory-management destructor

为了检查这一点,我运行了这个小测试代码。

#include <iostream>
using namespace std;

class name {
public:
  int data;
  name(int data) {
    this->data = data;
  }
  void printData() {
    cout << data << endl;
  }
};

int main() {
  name* namePointer;
  {
    name n(5);
    namePointer = &n;
  }
  namePointer->printData();
}

所以,有两个问题:

  1. name内的块中创建n对象main并保存其指针,以确保在我们离开块时调用其析构函数。但指针指向同一个对象,其成员函数仍可访问。这是不是意味着该对象尚未删除?

  2. 说我将其添加到我的name班级:

    ~name() {
        cout << "destructor called" << endl;
    }
    

    是否重写析构函数并在其中执行任何操作(此处为~name()),是否会阻止删除该对象?

  3. 编辑:感谢您的回复,这确实有所帮助。但我想知道这些投票的原因,我认为这是一个非常好的问题。 _(ツ)_ /¯

4 个答案:

答案 0 :(得分:4)

  

析构函数是解除内存还是实际删除对象

析构函数调用子对象的析构函数(如果有的话),执行析构函数体(如果已定义)。从技术上讲,解析器不会释放内存。可以在不释放的情况下调用destrcutor,并且可以在不调用析构函数的情况下释放内存。

当使用运算符delete删除动态对象时,将调用析构函数,然后释放内存。同样,当自动变量(例如您的n)的生命周期结束时,会调用其析构函数,并释放内存。

目前还不清楚&#34;删除对象&#34; 的含义。析构函数永远不会调用delete运算符(除非析构函数的主体{,子对象的}子对象调用delete运算符,但它不是this被删除的)

如果你的意思是析构函数是否会破坏对象,那么它是另一种方式:当一个对象被销毁时,它的析构函数将被调用。

  
      
  1. ...但指针指向同一个对象
  2.   

指针指向对象的位置。该物体已被破坏,不再存在。

  

...及其成员函数仍可访问。

成员函数的可访问性不依赖于指向的内容。取消引用未指向对象的指针(包括调用成员函数)具有未定义的行为。

  

这是不是意味着该对象尚未被删除?

没有。你不能从未定义的行为中得出结论。

  
      
  1. 是否覆盖了析构函数...
  2.   

这不是覆盖。覆盖是一个涉及继承和虚函数的特定语言术语。

您所做的是定义析构函数。

  
      
  1. ...并且不执行任何操作(在~name()中),防止删除对象?
  2.   

没有。在析构函数的主体中什么也不做就是隐式析构函数所做的事情(当你离开析构函数时,对象具有的那个未定义)。它不会阻止对象形式被删除&#34;。

答案 1 :(得分:3)

用C ++的说法,两个独立的概念是对象生命周期&#39;和“&#39;存储期限&#39;持续时间与生命周期几乎相同,但标准在此处使用两个不同的单词。存储是指对象占用的物理内存,而对象生存期是指允许访问对象的时间。

考虑到这一点,有两个人理解构造函数和析构函数确定对象的生命周期 - 在调用构造函数之前和调用析构函数之后不允许访问对象,并且不说存储生命周期。很明显,存储持续时间(或生命周期)不应小于对象的生命周期 - 一旦无法访问没有物理表示的对象 - 但可能比它长。

placement new与显式析构函数调用一起使用时很容易看到 - 存储仍然可用,但对象已经消失。

答案 2 :(得分:1)

在该块之后使用namePointer是未定义的行为,因为它是dangling pointer。之后(正确或不正确)的行为如何在不同的机器,编译器等上发生变化。

覆盖析构函数以输出一些文本不会改变对象的内存仍然被释放的事实。所以在这种情况下,n的析构函数将在块的末尾被调用,这将删除对象及其成员变量。

答案 3 :(得分:1)

  

1 ...这是不是意味着对象尚未删除?

没有

  

2 ...是否重写了析构函数,并且在其中不执行任何操作(此处&gt;在~name()中),是否阻止删除该对象?

没有

cout是你的朋友。

这是我的代码版本(我已经应用了我的名称编码标准)

#include <iostream>

class Name_t
{
   int m_data;
public:
   Name_t(int data) : m_data(data) {
      std::cout << "\n  Name_t::ctor" << std::endl;
   }
   ~Name_t() {
      std::cout << "\n  Name_t::dtor" << std::endl;
   }

   void cout() { std::cout << "\n  Name_t.cout() "
                           << m_data << std::endl; }
};

class T517_t
{
public:
   T517_t() = default;
   ~T517_t() = default;

   int exec()
      {
         Name_t* Name_tPointer = nullptr;
         {
            Name_t n(5);
            Name_tPointer = &n;
         }
         Name_tPointer->cout();
         return(0);
      }    
}; // class T517_t

int main(int , char** )
{
   int retVal = -1;
   {
      T517_t   t517;
      retVal = t517.exec();
   }
   return(retVal);
}

运行它,你会看到ctor和dtor都在UB之前运行(未定义的行为)。

  Name_t::ctor

  Name_t::dtor

  Name_t.cout() 5

另一个指标(通过cout)是清除或使dtor中的数据无效...除诊断期间我很少这样做。

替换dtor
   ~Name_t() {
      m_data = -1;
      std::cout << "\n  Name_t::dtor" << std::endl;
   }

可以看到dtor使您的数据无效。

  Name_t::ctor

  Name_t::dtor

  Name_t.cout() -1
  

析构函数是否释放内存?

哪个记忆?

否 - 对象的dtor不会释放对象。

是 - 对象的dtor应释放在ctor(Ying和Yang)中分配的任何内存,除非已经被实例的其他方法删除。