考虑下面的程序:
#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的要求都已满足:
要求:
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中(非规范性)“注释”中建议的实现方式,使用了类 enable_shared_from_this
中的私有 weak_ptr
成员。而且似乎不可能在 enable_shared_from_this
中做到这种问题的解决,除非采取更加复杂的方法。
这是标准中的缺陷吗?(如果是,就不需要在此评论应该采取什么解决方案:添加一个要求,使示例程序调用未定义行为,更改注释以不建议使用这样简单的实现方法,...)
lock2()
中对同一实例创建第二个新共享指针时,它将覆盖原始弱指针,当它解锁时,弱指针现在指向无效地址,因此出现错误。 - Dave Senable_from_this
的一个实现示例,其中第11段结论是:“创建独占指针的shared_ptr构造函数可以检测到enable_shared_from_this
基类的存在,并将新创建的shared_ptr
分配给其__weak_this
成员。”[我强调] 我觉得这个说明没有用“创建拥有指针”的说法来表达,这让我想知道什么是独特的shared_ptr
。 - Luc Dantonunique()
返回 true 的构造函数。基本上,这些构造函数从原始指针或 unique_ptr 中获取初始所有权。 - Dave Sshared_ptr
是被定义明确的;它的shared_ptr::unique
方法返回true
。即:use_count() == 1
的shared_ptr
。 - Nicol Bolas