调用析构函数是否显式地完全破坏了一个对象?

Rob*_*uld 14 c++ destructor

如果我显式地调用析构函数(myObject .~Object())这是否确保对象将被适当地销毁(调用所有子析构函数)?

好吧一些代码:

class Object
{
   virtual ~Object()
   {}
};

class Widget : public Object
{
   virtual ~Widget()
   {}
};

...
Object* aWidget = new Widget(); //allocate and construct
aWidget->~Object(); //destroy and DON'T deallocate
Run Code Online (Sandbox Code Playgroud)

我知道我可以删除该对象,但我不想这样做.我希望将分配的内存作为一项重要的优化保留.

谢谢!

the*_*jxc 16

答案是......几乎总是如此.

如果你的对象有一个非虚拟的析构函数,然后被子类化以添加需要释放的子元素...那么在对象基类上调用析构函数将不会释放子元素.这就是为什么你应该总是声明析构函数是虚拟的.

我们有一个有趣的案例,其中两个共享库引用了一个对象.我们更改了定义以添加需要释放的子对象.我们重新编译了第一个包含对象定义的共享库.

但是,第二个共享库没有重新编译.这意味着它不知道新添加的虚拟对象定义.从第二个共享库中调用的Delete简称为free,并且没有调用虚拟析构函数链.结果是令人讨厌的内存泄漏.

  • 好点:)但仍然不需要总是声明析构函数虚拟.只有在使用多态时才需要. (5认同)

Joh*_*ica 10

是.但圣洁的烟雾,你确定吗?如果是这样,我会使用展示位置new来构建你的Widget.使用放置new然后显式调用析构函数是一种可接受的,如果不寻常的习惯用法.

编辑:考虑手动分配内存,而不是使用new分配第一个对象,然后再重新使用其内存.这使您可以完全控制内存; 例如,你可以一次分配大块,而不是为每个块分配一个单独的内存块Widget.如果记忆确实是如此稀缺的资源,这将是公平的节省.

此外,也许更重要的是,您可以new"正常" 进行放置,而不是这种混合常规new/放置new解决方案.我不是说它不起作用,我只是说这是一个相当的,啊,创造性的解决你的记忆问题.


SPW*_*ley 6

是的,析构函数,即使被明确调用,也会正确地破坏它的子对象.

正如您似乎意识到的那样,这是一种罕见的行为,但也许作为经过良好测试和记录的库的一部分,它可能是有用的.但是文档(和配置文件)它虽然它是有效和安全的,但每个维护者(包括你)都不会对它感到满意.


0xC*_*ACE 6

是的,它将调用所有子析构函数,以便它可以按预期工作.

析构函数毕竟只是一个函数,只是在删除对象时调用它.

因此,如果您使用此方法,请注意以下事项:

#include <iostream>

class A
{
public: 
    A(){};
    ~A()
    {
        std::cout << "OMG" << std::endl;
    }
};

int main()
{
    A* a = new A;
    a->~A();
    delete a;
    return 0;
}

output:
OMG
OMG 
Run Code Online (Sandbox Code Playgroud)

实际上在对象上调用delete时,第二次调用析构函数,因此如果在析构函数中删除指针,请确保将它们设置为0,以便第二个调用析构函数时不会发生任何事情(因为删除null)指针什么都不做).


Tom*_*eys 5

请保存一些真正的麻烦并使用Boost对象池,这听起来像是源/接收器模式的现有实现.它将分配大块内存,将它们切成适合您对象的大小并将它们返回给您(在调用构造函数之后).当您删除对象时,它们会调用它们的析构函数,并将它们放入对象的链接列表中以供重用.它会自动增长和缩小,并确保对象的实例在内存中往往紧密相连.

如果不出意外,它是一个很好的示例实现,可以放置新的和明确使用的构造函数,您可以学习.