我无法弄清楚这一点。看起来我错过了一些简单的东西?我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 的引用数减少了 1)。请注意,我使用 int
只是为了简单,但它应该适用于不可复制的类型。
为什么?我有一个表示值的类,任何类型的值,用于一种编译器。实际值及其存储方式在实例化时是已知的。唯一知道的是类型。因此,该类存储一个包含稍后确定的值的占位符的shared_ptr(对应于在作为指针或引用传递时编译函数定义的参数:编译器只知道类型,仅此而已)。在运行时,占位符应替换为实际值。
编辑新的一天开始,想出了这个。我知道这很简单。
void MakePointToSameValue( Test& other )
{
other.ptr.swap( ptr );
ptr.reset();
ptr = other.ptr;
}
现在的另一个问题是:对于任何符合标准的指针,上述内容是否按预期工作?
这里需要两个级别的间接寻址。 虽然shared_ptr
所有对象都指向一个公共元数据块,其中包含计数和指向实际值的指针,这是正确的,但如果您尝试更新该块以指向不同的对象,您现在将有两个元数据块指向相同的值,每个元数据块对引用计数都有自己不同的想法。 使用每个元数据块的shared_ptr
对象都有正确的数量(从某种意义上说,它与引用计数匹配),因此每个块上的计数最终将达到零,但无法知道哪个块是最后一个达到零计数的块(因此应该删除该值)。 因此,shared_ptr
明智地不允许更改元数据中的对象指针。 您只能将shared_ptr
与新的元数据块、新计数、新对象相关联。 指向同一对象的其他指针不受影响。
执行此操作的正确方法是使用第二层间接寻址 ( shared_ptr<shared_ptr<int> >
)。 这样,只有一个元数据块,每个对象只有一个计数。 您的更新将发生在中间shared_ptr。
好吧,据我了解您的要求,shared_ptr
不包含任何此类机制; 任何常规类型也不会。如果你想要这种行为,你必须自己编写代码。我的建议:添加一个私有静态std::list<std::weak_ptr<Test>> registry
;通过将每个 Test
实例添加到构造函数中的registry
列表中来注册该实例,并确保在析构函数中删除它。
然后在 MakePointToSameValue
中使用该注册表循环访问所有实例并重置 ptr
的值。
如果你对效率感兴趣,并且实例数超过三个,你会想要用unordered_set
替换list
;也许在你的Test
类中使用unique_ptr
而不是shared_ptr
。
回答其他问题:不,它不起作用。查看reset()
的文档:它重置了一个特定的实例shared_ptr
:它对任何其他实例不做任何操作(也不知道)。当控制块中的参考计数达到零时,它会额外破坏点,仅此而已。