“this”指针在数据成员销毁期间有效吗?

PMo*_*bed 1 c++

让我们考虑下面的类。我并不是在问这是一种好做法还是坏做法。类的命名也是任意的,因此它不显示任何模式或设计。

class Proxy;

class ProxyImplementation
{
public:
    Proxy * proxy_;

    ProxyImplementation(Proxy * proxy): proxy_(proxy)
    {
    }

    ~ProxyImplementation()
    {
        // Is proxy_ valid here? or is it undefined behavior? 
        // is proxy_->number_ still valid here?  
    }


};

class Proxy
{
public:
    int number_;
    shared_ptr<ProxyImplementation> proxyImplementation_;
    Proxy()
    {
        number_ = 10;
        proxyImplementation_.reset(new ProxyImplementation(this));
    }

    ~Proxy() {}
};


Run Code Online (Sandbox Code Playgroud)

在主要

   Proxy *p = new Proxy();
   delete p;
Run Code Online (Sandbox Code Playgroud)

Proxy传递thisProxyImplementation其构造函数,当delete被调用时,Proxy析构函数被调用,然后按定义的顺序调用其成员析构函数。

this在成员析构函数期间有效吗?换句话说,访问proxy_中的指针是否有效~ProxyImplementation

Sha*_*ger 7

该标准保证成员按照声明的相反顺序被销毁。所有成员都必须在包含它们的对象被销毁之前被销毁(这通过检查是显而易见的;您不能销毁不再存在的内容)。因此,当Proxy实例需要自毁时,它必须:

  1. 调用析Proxy构函数(不执行任何操作)
  2. 执行清理proxyImplementation_
  3. 执行清理number_(对简单原语不执行任何操作)
  4. Proxy对对象本身执行最终清理

因此,如果proxyImplementation_是 a unique_ptr(并且指针始终指向原始Proxy),这足以说明两者number_Proxy对象本身必须仍然存在;proxyImplementation_它们在被完全摧毁之前不允许被摧毁。

问题是您使用了 a shared_ptr,这意味着在某个地方,shared_ptr同一ProxyImplementation对象可能有其他 s 。因此,当一个给定Proxy被销毁时,不能保证它所ProxyImplementation持有的内容会在那时被销毁。如果以后被破坏了,它就proxy_不再存在了,可怕的事情就会发生。

简而言之,如果您更改为使用unique_ptr在构建后从未替换的 ,这是安全的,如果您实际上共享shared_ptr. 如果您确实使用 aunique_ptr来确保安全,因为您将其声明为公共成员,我建议将其设置为正确初始化的const unique_ptr,这保证您或您的班级的任何消费者都不能(没有邪恶const_casts)替换unique_ptr'拥有的指针,因此它将始终是原始值。修正后的代码将是(更改行的注释):

class Proxy;

class ProxyImplementation
{
public:
    Proxy *const proxy_; // Explicitly declare constant pointer; 1-1 permanent
                         // relationship between Proxy and Implementation that
                         // consumers of your class can't change

    ProxyImplementation(Proxy *proxy): proxy_(proxy) {}

    ~ProxyImplementation()
    {
        // proxy_ is valid here
        // proxy_->number_ is valid here
    }
};

class Proxy
{
public:
    int number_;
    // Changed from shared_ptr to const unique_ptr: initialize once, can't be
    // reassigned, guaranteed to perform cleanup during destruction (no possibility
    // of shared_ptr copied by class consumer keeping it alive after Proxy is gone)
    const unique_ptr<ProxyImplementation> proxyImplementation_;

    // Use initializers, not assignment in constructor body, so const unique_ptr
    // can be assigned precisely once
    Proxy() : number_(10), proxyImplementation_(new ProxyImplementation(this)) {}
    // Or if you can guarantee C++14 or higher, and want new/delete-free code:
    Proxy() : number_(10),
              proxyImplementation_(std::make_unique<ProxyImplementation>(this)) {}

    // Got rid of explicitly declared empty destructor; let compiler generate it
};
Run Code Online (Sandbox Code Playgroud)