复制 boost::shared_ptr

5
typedef boost::shared_ptr<SomeData> data_ptr;
data_ptr cached_ptr;   // class member 
bool someWork(data_ptr& passed_ptr)
{
  // must copy passed_ptr = cached_ptr under some conditions
  // without pointing at the same memory 
  // I saw somewhere that I should do 
  // passed_ptr.reset(new SomeData(???))
  // I don't have a "reset" on passed_ptr
}

我查看了文档; 拷贝和转换构造函数
shared_ptr(shared_ptr const & r); // never throws
template<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws
Requires: Y* should be convertible to T*.

Effects: If r is empty, constructs an empty shared_ptr; otherwise,
         constructs a shared_ptr that shares ownership with r.

我不知道那是如何工作的 - 它是这样的吗?
passed_ptr = shared_ptr(cached_ptr);

? const应该放在哪里?它们共享所有权是什么意思?如果我修改“passed_ptr”,它不是一个拷贝,“cached_ptr”也会受影响吗?

我找不到例子...请帮忙。

谢谢。

3个回答

12

如果你有一个shared_ptr并将其分配给另一个shared_ptr,那么这两个共享指针将共享对象的所有权 - 这意味着所指对象所有权的引用计数将增加一。

事实上,在上一行中,你根本不需要构造临时共享指针。这就足够了:

passed_ptr = cached_ptr;

无论如何,使用拷贝构造函数来创建一个 shared_ptr 的基本逻辑与上面类似:你从一个 shared 指针开始,最终你将得到两个共享指向同一对象的 shared 指针(这意味着仅当这两个 shared 指针都被销毁时,该对象才会被销毁)。因此,当你这样做时:

passed_ptr = shared_ptr(cached_ptr);

最初您有一个共享指针(cached_ptr)指向给定对象,然后创建一个临时指针(将引用计数增加到2),再将其分配给passed_ptr(将引用计数增加到3),最后被销毁(将引用计数降回到2)。

另一方面,如果您想要的是将passed_ptr作为指向cached_ptr所指对象的副本的共享指针,则应该这样做(假设Data是可复制的):

passed_ptr = boost::make_shared<Data>(*cached_ptr);

或者,另一种选择:

passed_ptr.reset(new Data(*cached_ptr));

1
我想要一个实际的副本,而不是指向同一对象的2个指针。当我修改passed_ptr的数据时,cached_ptr的数据应该保持不变。 - Thalia
@Thalia:我扩展了我的答案来涵盖这个问题。 - Andy Prowl
@Thalia:很奇怪,你使用的boost版本是什么? - Andy Prowl
@Thalia:这个版本肯定有reset()成员函数,可以在这里看到。你是不是没有正确使用它?你得到了什么编译器错误? - Andy Prowl
1
@Thalia:我会使用 boost::make_shared<Data>(*cached_ptr)(别忘了 *)。另外,如果可以的话,建议切换到 C++11,它有 std::shared_ptr ;) - Andy Prowl
显示剩余5条评论

9

好的,让我们看看能否谈论一下这个问题。

std::shared_ptr<Data> x = std::make_shared<Data>();
std::shared_ptr<Data> y = x;

assert(x.get() == y.get());

如果我改变x指向的内容,那么y的信息也会因为它们指向了同一件事物而发生变化。
x->member = 3;
assert(x->member == 3);
assert(y->member == 3);

我可以更改x指向的内容,而不会更改y指向的内容。

x = std::make_shared<Data>();
assert(x.get() != y.get());

如果我这样做,x 的更改不会反映在 y 上。因为它们指向不同的东西。
x->member = 4;
assert(x->member == 4);
assert(y->member != 4);

如果我想复制x的内容,并将其存储在y中,那么我需要创建一个新的共享对象。

y = std::make_shared<Data>(*x);

此时,xy都将该成员变量设置为4。因为x已经设置了它,而我们使用*x的内容创建了*y

assert(x->member == 4);
assert(y->member == 4);

但是,由于xy指向内存中的不同位置,我们可以更改它们之一。

assert(x.get() != y.get();
x->member = 3;
assert(x->member == 3);
assert(y->member == 4);

如果我将一个shared_ptr传递给一个函数,那么这就像第一种情况一样。
void func(std::shared_ptr<Data> z) {
    assert(x.get() == z.get());
}

func(x);

我还可以使用 reset() 来释放特定 shared_ptr 的内容,这将导致 shared_ptr 指向的值变为 NULL。

x.reset();
assert(x.get() == NULL);

关于常量正确性(const correctness),它有点奇怪。

const std::shared_ptr<Data> x;
x.reset(); // Fails because the shared_ptr is const.
x->member = 3; // Succeeds because Data is not const.

std::shared_ptr<const Data> x;
x.reset(); // Succeeds because the shared_ptr is not const.
x->member = 3; // Fails because Data is const.

我可以将 shared_ptr 传递给函数而不会导致第一种情况吗? - simplename
当然,通过引用传递它。 - Bill Lynch

1
通常的惯例是传递一个 const 的 shared_ptr:
bool
someWork( data_ptr const& passed_ptr )
{
    //  ...
}

这允许使用指针(包括复制和转换),但不允许修改它。

如果您想在调用现场更改指针所指向的内容,则必须传递非const引用。 在这里最简单的策略是将新值分配给它,但您也可以调用reset函数。

至于它是如何工作的:当您将指针传递给引用参数时,您只是提供了被调用函数访问原始指针的权限。 在幕后,它非常像系统性解除引用的指针。 而且,您似乎混淆了指针和指针所指向的内容;共享的是它所指向的内容,而不是指针本身。 指针本身的行为与任何其他C ++对象完全相同。


我没有将它设为const,因为它的内容会发生变化 - 如果我将其设为const,我仍然可以更改它所包含的数据吗? - Thalia
@Thalia:您不能更改指针本身的状态(例如,分配给它),但可以更改所指向对象的状态。 - Andy Prowl
那么,我如何将cached_ptr的内容复制到passed_ptr的内容中,而不是将一个指针赋值给另一个指针呢? - Thalia
@Thalia:你不需要这样做。在这种情况下,接受一个非const的引用是可以的。更好的方法是从你的函数返回一个共享指针(如果bool用于表示成功/失败,你可以考虑使用异常代替)。 - Andy Prowl
@Thalia 如果你不清楚地解释你想做什么,那么很难解释如何去做。你想修改指针还是它所指向的内容?在C++中,两者都是对象,都可以是const。换句话说,你可以有一个shared_ptr<Data> const&,它是一个指向非const对象的const指针的引用,或者你可以有一个shared_ptr<Data const>&,它是一个指向const对象的非const指针的引用。(当然,还有shared_ptr<Data>&,其中没有任何const,以及shared_ptr<Data const> const&,全部都是const。) - James Kanze
谢谢,我现在明白了并会在参数中添加一个const。 - Thalia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接