是否允许C++编译器优化器破坏我的析构函数多次被调用?

sha*_*oth 4 c++ compiler-construction optimization

我们曾经接受过一位非常有经验的C++开发人员的采访,他无法回答以下问题:是否有必要从C++中的派生类析构函数中调用基类析构函数?

显然答案是否定的,C++无论如何都会自动调用基类析构函数.但是如果我们试图打电话呢?正如我所看到的,结果将取决于是否可以在不调用错误行为的情况下调用基类析构函数两次.

例如,在这种情况下:

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

class DerivedSafe {
public:
    ~DerivedSafe()
    {
        BaseSafe::~BaseSafe();
    }
};
Run Code Online (Sandbox Code Playgroud)

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

但在这种情况下:

class BaseUnsafe {
public:
    BaseUnsafe()
    {
       buffer = new char[100];
    }
    ~BaseUnsafe ()
    {
        delete[] buffer;
    }
private:
    char* buffer;
};

class DerivedUnsafe {
public:
    ~DerivedUnsafe ()
    {
        BaseUnsafe::~BaseUnsafe();
    }
};
Run Code Online (Sandbox Code Playgroud)

explicic调用将运行正常,但是对析构函数的隐式(自动)调用将触发双删除和未定义的行为.

看起来在第二种情况下很容易避免使用UB.之后只需设置buffer为空指针delete[].

但这会有帮助吗?我的意思是析构函数只能在完全构造的对象上运行一次,因此优化器可以决定设置buffer为空指针是没有意义的,并且消除了使程序暴露双重删除的代码.

编译器是否允许这样做?

And*_*nck 13

标准12.4/14

一旦为对象调用析构函数,该对象就不再存在; 如果为生命周期结束的对象调用析构函数,则行为未定义(3.8).

所以我想编译器应该可以自由地将缓冲区的设置优化为null,因为在调用析构函数之后对象不再存在.

但即使编译器没有删除缓冲区为null的设置,似乎两次调用析构函数也会导致UB.