考虑这个程序:
#include <memory>
#include <iostream>
class X
: public std::enable_shared_from_this<X>
{
public:
struct Cleanup1 { void operator()(X*) const; };
struct Cleanup2 { void operator()(X*) const; };
std::shared_ptr<X> lock1();
std::shared_ptr<X> lock2();
};
std::shared_ptr<X> X::lock1()
{
std::cout << "Resource 1 locked" << std::endl;
return std::shared_ptr<X>(this, Cleanup1());
}
std::shared_ptr<X> X::lock2()
{
std::cout << "Resource 2 locked" << std::endl;
return std::shared_ptr<X>(this, Cleanup2());
}
void X::Cleanup1::operator()(X*) const
{
std::cout << "Resource 1 unlocked" << std::endl;
}
void X::Cleanup2::operator()(X*) const
{
std::cout << "Resource 2 unlocked" << std::endl;
}
int main()
{
std::cout << std::boolalpha;
X x;
std::shared_ptr<X> p1 = x.lock1();
{
std::shared_ptr<X> p2 = x.lock2();
}
}
我在 C++11 标准第 20.7.2 节中没有看到任何内容表明其中任何内容都是无效的。shared_ptr
让两个对象存储相同的指针&x
但不共享所有权并使用不会结束生命周期的“删除器”有点不寻常*get()
,但没有任何禁止它。(如果其中任何一个完全不是故意的,就很难解释为什么某些shared_ptr
成员函数接受一个std::nullptr_t
值。)正如预期的那样,程序输出:
Resource 1 locked
Resource 2 locked
Resource 2 unlocked
Resource 1 unlocked
但现在如果我添加一点main()
:
int main()
{
std::cout << std::boolalpha;
X x;
std::shared_ptr<X> p1 = x.lock1();
bool test1( x.shared_from_this() );
std::cout << "x.shared_from_this() not empty: " << test1 << std::endl;
{
std::shared_ptr<X> p2 = x.lock2();
}
try {
bool test2( x.shared_from_this() );
std::cout << "x.shared_from_this() not empty: " << test2 << std::endl;
} catch (std::exception& e) {
std::cout << "caught: " << e.what() << std::endl;
}
}
然后事情变得更加棘手。使用 g++ 4.6.3,我得到输出:
Resource 1 locked
x.shared_from_this() not empty: true
Resource 2 locked
Resource 2 unlocked
caught: std::bad_weak_ptr
Resource 1 unlocked
为什么第二次调用会shared_from_this()
失败?满足 20.7.2.4p7 的所有要求:
Requires:
enable_shared_from_this<T>
应该是一个可访问的基类T
。*this
应该是t
类型对象的子对象T
。应至少有一个shared_ptr
实例p
拥有.&t
[T
是X
,t
是x
,p
是p1
。]
但是 g++enable_shared_from_this
基本上遵循 20.7.2.4p10 中(非规范)“注释”中建议的实现,使用weak_ptr
class 中的私有成员enable_shared_from_this
。如果不做一些相当复杂的事情,似乎不可能解决这类问题enable_shared_from_this
。
这是标准中的缺陷吗?(如果是这样,这里不需要评论解决方案“应该”是什么:添加一个要求以便示例程序调用未定义的行为,将注释更改为不建议这样一个简单的实现就足够了,....)