调用析构函数或调用`delete`时是否释放内存?

bob*_*obo 15 c++ destructor memory-management

假设你有一个对象class Fool.

class Fool
{
    int a,b,c;
    double* array ;
    //...
    ~Fool()
    {
        // destroys the array..
        delete[] array ;
    }
};


Fool *fool = new Fool() ;
Run Code Online (Sandbox Code Playgroud)

现在,我知道你不应该,但有些傻瓜fool无论如何都会调用析构函数. fool->~Fool();.

这是否意味着fool释放了内存,(即a,b,c无效)或者这是否意味着只有~Fool()函数中的解除分配(即数组只被删除?)

所以我想我的问题是,析构函数只是delete在对象上调用时调用的另一个函数,还是它做了更多?

tem*_*def 30

如果你写

fool->~Fool();
Run Code Online (Sandbox Code Playgroud)

您结束对象的生命周期,它会调用析构函数并回收内部array数组.但是,没有释放持有对象的内存,这意味着如果您想使用placement new将对象恢复生命:

new (fool) Fool;
Run Code Online (Sandbox Code Playgroud)

你可以这样做.

根据规范,fool在显式调用析构函数之后读取或写入字段的值会导致未定义的行为,因为对象的生命周期已经结束,但是仍然应该分配持有该对象的内存,并且您需要通过调用来释放它operator delete:

fool->~Fool();
operator delete(fool);
Run Code Online (Sandbox Code Playgroud)

使用的原因operator delete而不仅仅是写作

delete fool;
Run Code Online (Sandbox Code Playgroud)

是后者有不确定的行为,因为它fool的生命已经结束.使用原始释放例程operator delete可确保回收内存,而无需尝试执行任何操作来结束对象的生命周期.

当然,如果对象的内存不是来自new(可能是堆栈分配,或者您可能正在使用自定义分配器),那么您不应该使用operator delete它来释放它.如果你这样做了,你最终会得到未定义的行为(再次!).这似乎是这个问题中反复出现的主题.:-)

希望这可以帮助!

  • 我不确定你的描述是完全对称的.如果我说`char buf [20000]; 傻瓜*f =新(buf + 17)傻瓜;`,然后我当然可以*不*调用`operator delete(f);`.这就是重点,分配与构造无关,只有获取记忆的人才能决定如何处置它. (2认同)
  • @Kerrek SB-我不确定我理解你的上一条评论.我说内部数组是回收的,因为在析构函数中,有一个对`delete []`的调用.我假设这个数组已正确分配.我错过了一个重要的细节吗?或者我误解了你的最后评论? (2认同)

Ker*_* SB 8

析构函数调用就是这样,它调用析构函数.没有更多,也没有更少.分配与构造分开,并从破坏中解除分配.

典型的顺序是这样的:

1. Allocate memory
2. Construct object
3. Destroy object  (assuming no exception during construction)
4. Deallocate memory
Run Code Online (Sandbox Code Playgroud)

实际上,如果你手动运行它,你将不得不自己调用析构函数:

void * addr = ::operator new(sizeof(Fool));
Fool * fp = new (addr) Fool;
fp->~Fool();
::operator delete(addr);
Run Code Online (Sandbox Code Playgroud)

写这个的自动方式当然是Fool * fp = new Fool; delete fp;.该new表达式调用分配和建设为你和delete表达调用析构函数并释放内存.