我们曾经采访过一位经验丰富的C ++开发人员,他无法回答以下问题:是否有必要从C ++的派生类析构函数中调用基类析构函数? ,无论如何,C ++都会自动调用基类的析构函数。但是,如果我们尝试拨打电话怎么办?如我所见,结果将取决于是否可以调用基类析构函数两次而不会调用错误行为。

例如,在这种情况下:

class BaseSafe {
public:
    ~BaseSafe()
    {
    }
private:
    int data;
};

class DerivedSafe {
public:
    ~DerivedSafe()
    {
        BaseSafe::~BaseSafe();
    }
};


一切都会好起来-可以安全地两次调用BaseSafe析构函数,程序可以正常运行。

但是在这种情况下:
显式调用可以正常运行,但是对析构函数的隐式(自动)调用将触发重复删除和未定义的行为。第二种情况。只需将buffer后面的delete[]设置为空指针即可。我的意思是,析构函数只应在完全构造的对象上运行一次,因此优化程序可以决定将buffer设置为null指针没有任何意义,并消除将程序暴露给双删除的代码。

允许编译器这样做吗?

评论

两个问题:(1)甚至允许您显式调用基类的析构函数吗?我认为VC ++不会允许您使用。 (2)为什么仍然要这样做?

@简:(1)是的。您可以显式调用基本析构函数,因为有一条通用规则允许它。您最好以后再使用一个不抛出异常的ctor来替换被破坏的对象。 (2)打我。以后需要重建时为什么要破坏它。

肯定是。从技术上讲,在具有良好运行时内存管理系统的多线程系统中,我认为应该已经分配了那部分内存。

@sharptooth:在单个对象上被多次调用不是析构函数的属性,而是构造函数的属性,因此没有什么坏处。可以显式调用析构函数没什么区别。

#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