让我们考虑下面的类。我并不是在问这是一种好做法还是坏做法。类的命名也是任意的,因此它不显示任何模式或设计。
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传递this给ProxyImplementation其构造函数,当delete被调用时,Proxy析构函数被调用,然后按定义的顺序调用其成员析构函数。
this在成员析构函数期间有效吗?换句话说,访问proxy_中的指针是否有效~ProxyImplementation?
该标准保证成员按照声明的相反顺序被销毁。所有成员都必须在包含它们的对象被销毁之前被销毁(这通过检查是显而易见的;您不能销毁不再存在的内容)。因此,当Proxy实例需要自毁时,它必须:
Proxy构函数(不执行任何操作)proxyImplementation_number_(对简单原语不执行任何操作)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)
| 归档时间: |
|
| 查看次数: |
220 次 |
| 最近记录: |