如何使shared_ptr的所有副本等于另一个shared_ptr?

时间:2012-07-03 12:11:20

标签: c++ shared-ptr smart-pointers

我无法理解这一点..看起来我错过了一些简单的东西? 我应该在MakePointToSameValue放置什么,以便在(1)

  • b.ptr和c.ptr都指向a.ptr
  • 换句话说,a.ptr.get() == b.ptr.get() == c.ptr.get()
  • b.ptr最初指向的值和c.ptr被删除

struct Test
{
public:
  Test( int val ) :
    ptr( std::make_shared< int >( val ) )
  {
  }

  void MakePointToSameValue( Test& other )
  {
    //what do I put here?
    //other.ptr = this->ptr; //doesn't do it
  }

private:
  std::shared_ptr< int > ptr;
};

Test a( 0 );
Test b( 5 );
Test c( b );

b.MakePointToSameValue( a );

//(1)

复制ptr不起作用,因为它不会改变c(好吧,c.ptr它的refcount减少了一个)。请注意,我使用int只是为了简单,但它应该适用于不可复制的类型。

为什么呢?我有一个表示值的类,任何类型的值,用于某种编译器。当实例化时,实际值以及它的存储方式是已知的。唯一知道的是类型。因此,类存储一个shared_ptr,其中包含一个占位符,用于稍后确定的值(对应于在作为指针或引用传递时编译函数定义的参数:编译器只知道类型,仅此而已)。在运行时,占位符应替换为实际值。

编辑新的一天开始,想出了这个。我知道这很简单。

void MakePointToSameValue( Test& other )
{
  other.ptr.swap( ptr );
  ptr.reset();
  ptr = other.ptr;
}

现在的另一个问题是:上述工作是否符合预期的任何标准兼容指针?

2 个答案:

答案 0 :(得分:3)

这里需要两个间接级别。虽然你是对的,所有shared_ptr个对象都指向一个包含计数和指向实际值的指针的公共元数据块,如果你试图更新该块以指向另一个对象,你现在已经拥有了两个元数据块指向相同的值,每个元数据块都有自己对引用计数的不同概念。使用每个元数据块的shared_ptr个对象的正确数字(在某种意义上它与引用计数匹配),因此每个块上的计数最终将达到零,但是无法知道哪个块是最后一个块达到零计数(因此应删除该值)。所以shared_ptr明智地不允许更改元数据中的对象指针。您只能将shared_ptr与新的元数据块,新计数,新对象相关联。其他指向同一对象的指针不受影响。

正确的方法是使用第二层间接(shared_ptr<shared_ptr<int> >)。这样,每个对象只有一个元数据块和一个计数。您的更新将发生在中间shared_ptr。

答案 1 :(得分:1)

嗯,据我了解你的要求,shared_ptr不包含任何此类机制;也不会有任何常规类型。如果您想要这种行为,则必须自己编写代码。我的建议:添加私有静态std::list<std::weak_ptr<Test>> registry;通过将每个Test实例添加到构造函数中的registry列表中来注册它们,并确保在析构函数中将其删除。

然后在MakePointToSameValue中使用该注册表来遍历所有实例并重置ptr的值。

如果您对效率感兴趣并且实例多于三个,则您需要将list替换为unordered_set;并且可能在unique_ptr课程中使用shared_ptr而不是Test

回答其他问题:不,它不起作用。查看reset()的文档:它重置一个特定的实例shared_ptr:它对任何其他实例都没有任何作用(并且不知道)。当控制块中的引用计数达到零时,它还会破坏指针对象,但就是这样。