假设您有一个class Fool
的对象。
class Fool
{
int a,b,c;
double* array ;
//...
~Fool()
{
// destroys the array..
delete[] array ;
}
};
Fool *fool = new Fool() ;
现在,I know you shouldn't,但是有些傻瓜无论如何都会调用fool
上的析构函数。 fool->~Fool();
。
这是否意味着fool
的内存被释放(即a,b,c无效)或这意味着只有~Fool()
函数中的解除分配发生(即只删除数组?)
所以我想我的问题是,析构函数只是在对象上调用delete
时被调用的另一个函数,还是它做了更多?
答案 0 :(得分:30)
如果你写
fool->~Fool();
结束对象的生命周期,该生命周期调用析构函数并回收内部array
数组。但是,没有释放持有对象的内存,这意味着如果你想使用placement new将对象恢复生命:
new (fool) Fool;
你可以这样做。
根据规范,在显式调用析构函数后读取或写入fool
字段的值会导致未定义的行为,因为对象的生命周期已经结束,但仍应分配持有该对象的内存,您需要通过调用operator delete
:
fool->~Fool();
operator delete(fool);
使用operator delete
而非仅写
delete fool;
后者有未定义的行为,因为fool
的生命周期已经结束。使用原始释放例程operator delete
可确保回收内存,而无需尝试执行任何操作来结束对象的生命周期。
当然,如果对象的内存不是来自new
(也许它是堆栈分配的,或者你可能正在使用自定义分配器),那么你不应该使用{{1}释放它。如果你这样做了,你最终会得到未定义的行为(再次!)。这似乎是这个问题中反复出现的主题。 : - )
希望这有帮助!
答案 1 :(得分:8)
析构函数调用就是这样,它调用析构函数。没有更多,也没有更少。分配与构造分开,并从销毁中解除分配。
典型的顺序是:
1. Allocate memory
2. Construct object
3. Destroy object (assuming no exception during construction)
4. Deallocate memory
事实上,如果你手动运行,你将 自己调用析构函数:
void * addr = ::operator new(sizeof(Fool));
Fool * fp = new (addr) Fool;
fp->~Fool();
::operator delete(addr);
自动编写方式当然是Fool * fp = new Fool; delete fp;
。 new
表达式为您调用分配和构造,delete
表达式调用析构函数并释放内存。
答案 2 :(得分:2)
这是否意味着傻瓜的记忆被释放,(即a,b,c无效)或是 这意味着只有~Fool()函数中出现的解除分配(即 数组只被删除?)
Fool::~Fool()
对Fool
实例是否存储在动态存储中(通过new
)或是否存储在自动存储(即堆栈对象)中没有任何了解。由于对象在运行析构函数后不再存在,因此在析构函数退出后,您不能认为a
,b
,c
和array
将保持有效。 / p>
但是,因为Fool::~Fool()
对Fool
的分配方式一无所知,所以直接在new
- 分配Fool
上调用析构函数将不会释放支持的基础内存对象。
答案 3 :(得分:0)
在析构函数被调用之后,你应不访问a
,b
和c
,即使它是一个显式的析构函数调用。您永远不会知道编译器在析构函数中放置的可能使这些值无效的内容。
但是,在显式析构函数调用的情况下,实际上并没有释放内存。这是设计的;它允许清理使用展示位置new
构建的对象。
示例:
char buf[sizeof (Fool)];
Fool* fool = new (buf) Fool; // fool points to buf
// ...
fool->~Fool();
答案 4 :(得分:0)
通过delete
看到析构函数与释放不同的最简单的地方是首先自动分配:
{
Fool fool;
// ~Fool called on exit from block; nary a sign of new or delete
}
另请注意,STL容器充分利用了显式析构函数调用。例如,std::vector<>
完全独立地处理存储和包含对象的生命周期。