是C ++编译器优化器允许打破我的析构函数被调用多次吗? [英] Is the C++ compiler optimizer allowed to break my destructor ability to be called multiple times?

查看:221
本文介绍了是C ++编译器优化器允许打破我的析构函数被调用多次吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们曾经接触过一个经验丰富的C ++开发人员,他无法回答以下问题:有必要从C ++中的派生类析构函数调用基类析构函数



显然答案是否定的,C ++将自动调用基类析构函数。但是如果我们试图打电话怎么办?正如我看到的,结果将取决于是否可以调用基类析构函数,而不调用错误的行为。



例如在这种情况下:

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

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

一切都会好的 - BaseSafe 析构函数



但在这种情况下:

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

class DerivedUnsafe {
public:
〜DerivedUnsafe()
{
BaseUnsafe ::〜BaseUnsafe();
}
};

显式调用会正常运行,但是对析构函数的隐式(自动)调用将触发double - 删除和未定义的行为。



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



<但是这会有帮助吗?我的意思是析构函数只能在完全构造的对象上运行一次,所以优化器可以决定将 buffer 设置为空指针是没有意义的,程序双重删除。



编译器是否允许这样做?

解决方案

Standard 12.4 / 14


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


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



如果缓冲区的设置为null没有被编译器删除,似乎调用析构函数两次将导致UB。


We once had an interview with a very experienced C++ developer who couldn't answer the following question: is it necessary to call the base class destructor from the derived class destructor in C++?

Obviously the answer is no, C++ will call the base class destructor automagically anyway. But what if we attempt to do the call? As I see it the result will depend on whether the base class destructor can be called twice without invoking erroneous behavior.

For example in this case:

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

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

everything will be fine - the BaseSafe destructor can be called twice safely and the program will run allright.

But in this case:

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

class DerivedUnsafe {
public:
    ~DerivedUnsafe ()
    {
        BaseUnsafe::~BaseUnsafe();
    }
};

the explicic call will run fine, but then the implicit (automagic) call to the destructor will trigger double-delete and undefined behavior.

Looks like it is easy to avoid the UB in the second case. Just set buffer to null pointer after delete[].

But will this help? I mean the destructor is expected to only be run once on a fully constructed object, so the optimizer could decide that setting buffer to null pointer makes no sense and eliminate that code exposing the program to double-delete.

Is the compiler allowed to do that?

解决方案

Standard 12.4/14

Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended (3.8).

So I guess the compiler should be free to optimize away the setting of buffer to null since the object no longer exists after calling the destructor.

But even if the setting of the buffer to null wasn't removed by the compiler, it seems like calling the destructor twice would result in UB.

这篇关于是C ++编译器优化器允许打破我的析构函数被调用多次吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆