例如,在这种情况下:
class BaseSafe {
public:
~BaseSafe()
{
}
private:
int data;
};
class DerivedSafe {
public:
~DerivedSafe()
{
BaseSafe::~BaseSafe();
}
};
一切都会好起来-可以安全地两次调用
BaseSafe
析构函数,程序可以正常运行。但是在这种情况下:
显式调用可以正常运行,但是对析构函数的隐式(自动)调用将触发重复删除和未定义的行为。第二种情况。只需将
buffer
后面的delete[]
设置为空指针即可。我的意思是,析构函数只应在完全构造的对象上运行一次,因此优化程序可以决定将buffer
设置为null指针没有任何意义,并消除将程序暴露给双删除的代码。允许编译器这样做吗?
#1 楼
标准12.4 / 14一旦为
对象调用了析构函数,则该对象不再存在;
如果
析构函数的行为是不确定的为寿命已到期(3.8)的对象调用
。
因此,我猜编译器应该可以自由地将缓冲区设置优化为null,因为对象no
但是,即使编译器没有删除缓冲区为null的设置,似乎两次调用析构函数也会导致UB。
评论
您的解释是正确的。在调用第一个析构函数之后,对象生存期结束(3.8),并在第二次调用时应用12.4 / 14。 dtor内部发生的事情无关紧要。
– MSalters
2010年5月4日,10:47
#2 楼
调用析构函数会将对象转换为原始内存。您无法破坏原始内存;这是不确定的行为。 C ++编译器有权执行任何所需的操作。虽然不太可能将您的计算机变成奶酪,但它可能会故意触发手掌的SEGFAULT(至少在调试模式下)。评论
“ C ++编译器有权执行任何想要的操作。”不是真的,但是它特别有权执行合法程序无法检测到的任何事情,并且似乎没有合法方法来检测是否存在空分配。因此,从理论上讲,它可能会这样做。实际上,我认为这不太可能。但是,整个用例不太可能出现。我在C ++编程的第一天就犯了一个错误,但此后再也没有。
–user207421
2010年5月4日7:04
@EJP:是的,真的!未定义的行为表示所有投注均无效。编译器可能引起任何行为,即使它犯了危害人类罪,违反自然法则或倒退并杀死您的祖父,它仍然会符合标准,即使它无效(实际上,也许不是最后一个)。为了证明这一点,如果gcc的早期版本看到#pragma,则偶尔会尝试通过玩河内塔楼游戏来启动emacs。
–马塞洛·坎托斯(Marcelo Cantos)
2010年5月4日10:51
评论
两个问题:(1)甚至允许您显式调用基类的析构函数吗?我认为VC ++不会允许您使用。 (2)为什么仍然要这样做?@简:(1)是的。您可以显式调用基本析构函数,因为有一条通用规则允许它。您最好以后再使用一个不抛出异常的ctor来替换被破坏的对象。 (2)打我。以后需要重建时为什么要破坏它。
肯定是。从技术上讲,在具有良好运行时内存管理系统的多线程系统中,我认为应该已经分配了那部分内存。
@sharptooth:在单个对象上被多次调用不是析构函数的属性,而是构造函数的属性,因此没有什么坏处。可以显式调用析构函数没什么区别。