asc*_*ler 11 c++ shared-ptr c++11
考虑这个程序:
#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中的(非规范的)"注释"的建议实现,使用weak_ptr类中的私有成员enable_shared_from_this.如果不做一些相当复杂的事情,似乎不可能解决这类问题enable_shared_from_this.
这是标准中的缺陷吗?(如果是这样,这里不需要注释解决方案"应该"是什么:添加一个需求,以便示例程序调用未定义的行为,将注释更改为不建议这样一个简单的实现就足够了,....)
是的,C ++ 11中存在缺陷。在允许这样做:
有两个shared_ptr对象存储相同的指针&x但不共享所有权,并使用不会终止* get()生命周期的“删除器”,但是没有什么禁止它,这有点不寻常。
无论 “删除程序”做什么,都应明确声明这是未定义的行为。当然,以这种方式做事在技术上可能并不违法。
但是,您对使用代码的人撒谎。收到a的任何人的期望shared_ptr都是他们现在拥有该对象的所有权。只要他们保留该对象shared_ptr(或其副本),它指向的对象仍然存在。
您的代码不是这种情况。因此,我想说它在语法上是正确的,但在语义上是无效的。
的语言shared_from_this很好。这shared_ptr是需要更改的语言。应该声明创建“拥有”同一指针的两个单独的唯一指针是未定义的行为。
我同意这是规范中的一个漏洞,因此是一个缺陷。它与http://open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2179基本相同,尽管这个问题来自一个稍微不同的(恕我直言更明显是破碎的)角度。
我不确定我是否同意这是对 的滥用shared_ptr,我认为使用 shared_ptrs 这样做很好,因为与 issue 2179 中的代码不同,您使用的是无操作删除器。我认为这个问题是当你尝试那种利用结合起来shared_ptr用enable_shared_from_this。
所以我的第一个想法是通过扩展以下要求来修复它shared_from_this:
要求:
enable_shared_from_this<T>应是 的可访问基类T。*this应该是t类型对象的子对象T。应至少有一个拥有的shared_ptr实例p,拥有的&t任何其他shared_ptr实例&t应与 共享所有权p。
但这还不够,因为您的示例满足该要求:在第二次调用时shared_from_this()只有一个所有者 ( p1) 但您已经enable_shared_from_this通过调用lock2().
该程序的较小形式是:
#include <memory>
using namespace std;
int main()
{
  struct X : public enable_shared_from_this<X> { };
  auto xraw = new X;
  shared_ptr<X> xp1(xraw);   // #1
  {
    shared_ptr<X> xp2(xraw, [](void*) { });  // #2
  }
  xraw->shared_from_this();  // #3
}
libstdc++、libc++ 和 VC++ (Dinkumware) 的所有三个都表现相同并bad_weak_ptr在 #3 处抛出,因为在 #2 处,它们更新weak_ptr<X>基类的成员以使其与 共享所有权xp2,这超出了范围而使weak_ptr<X>处于过期状态.
有趣的boost::shared_ptr是不会抛出,而是 #2 是一个空操作,#3 返回一个shared_ptr与xp1. 这样做是为了响应一个错误报告,其中的示例与上面的示例几乎完全相同。