【问题标题】:C++ - Does the Destructor just deallocate the memory or does it actually delete the object?C++ - 析构函数只是释放内存还是实际上删除了对象?
【发布时间】:2018-01-09 19:36:51
【问题描述】:

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

#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 对象nmain 内部的块中创建并保存其指针,以确保在我们离开块时调用其析构函数。但是指针指向同一个对象,并且它的成员函数仍然可以访问。这不是说对象还没有被删除吗?

  2. 假设我将其添加到我的name 课程中:

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

    重写析构函数并在其中不做任何事情(此处为~name())是否会阻止对象被删除?

编辑:感谢您的回复,这真的很有帮助。但是我想知道这些反对票的原因吗,我认为这是一个非常好的问题。 ¯_(ツ)_/¯

【问题讨论】:

  • 不要将“价值”与“记忆”混为一谈。析构函数调用(应该)只会使对象的值无效。
  • 析构函数only 销毁并清理对象。它不会释放内存。这是由析构函数之外的单独机制管理的。
  • 我推荐阅读这个:isocpp.org/wiki/faq/dtors
  • @matrixisreal 您永远不会在堆上分配任何内存,并且您引用超出范围的堆栈变量,该变量的内存被清理然后与您的析构函数完全分离
  • 我不同意重复。他们谈论在对象被删除后访问对象,但问题是“析构函数只是释放内存还是实际删除对象”。提供的重复不回答这个问题。

标签: c++ pointers memory-management destructor


【解决方案1】:

析构函数只是释放内存还是实际删除对象

析构函数调用子对象的析构函数(如果有),执行析构函数体(如果定义了)。从技术上讲,释放内存的不是析构函数。析构函数可以在不释放的情况下调用,内存可以在不调用析构函数的情况下释放。

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

不清楚您所说的“删除对象”是什么意思。析构函数永远不会调用 delete 运算符(除非析构函数的主体 {,of a sub object} 子对象调用 delete 运算符,但被删除的不是 this)。

如果你的意思是析构函数是否销毁对象,那么反过来说:当一个对象被销毁时,它的析构函数会被调用。

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

指针指向对象所在的位置。该对象已被销毁,不再存在。

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

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

这不是说对象还没有被删除吗?

没有。您无法从未定义的行为中得出结论。

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

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

你所做的是定义一个析构函数。

  1. ... 并且在其中什么都不做(在 ~name() 中),防止对象被删除?

没有。在析构函数的主体中什么都不做与隐式析构函数所做的完全相同(当您未定义析构函数时对象所具有的那个)。它不会“防止对象表单被删除”。

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 我只想说“内存”,而不是“物理内存”,因为大多数时候它是 虚拟内存(不是物理 RAM)
    • @BasileStarynkevitch,太深入了。有人可能会争辩说,虚拟内存在简单意义上也是物理的,因为它由 RAM(或任何其他内存,如 MMC)或交换支持 - 这两者都是相当物理的。我的观点是,存储是物质的,可以这么说。我不知道如何恰当地表达这一点,恐怕我们无法真正定义“材料”的含义。
    【解决方案3】:

    在该块之后使用namePointer 是未定义的行为,因为它是dangling pointer。之后它的行为方式(正确或错误)可能会在不同的机器、编译器等上发生变化。

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

    【讨论】:

      【解决方案4】:

      1 ... 这不是说对象还没有被删除吗?

      没有。

      2 ... 是否重写析构函数,并且在其中什么也不做(这里> 在~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
      

      析构函数会释放内存吗?

      哪个内存?

      No - 对象的 dtor 不会解除分配对象。

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

      【讨论】:

      • UB - 在这种情况下,它是一个悬空指针的使用,一个指向已被 dtor'd 的对象的指针
      猜你喜欢
      • 1970-01-01
      • 2016-10-25
      • 2011-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-05
      • 2013-10-04
      • 2013-02-26
      相关资源
      最近更新 更多