C ++共享指针引用数组中的对象

时间:2014-07-23 20:35:34

标签: c++ arrays pointers smart-pointers

刚刚开始研究智能指针以及它们如何用于提高代码内的效率,我很好奇shared_ptr如何对数组/向量/容器中的类对象作出反应。

从我所读到的,共享指针的一般要点是,一旦指向一个对象的所有指针都被销毁或重新分配,该对象也会被破坏。但是,如果我们的对象也存储在数组/向量中,那么共享指针仍然会尝试销毁该对象,即使它仍然可以访问。

是否有某种方法可以设置包含数组的共享指针,这样您就可以确保在清除数组/向量后只删除对象,或者是否必须在数组中使用shared_ptr ?

3 个答案:

答案 0 :(得分:2)

不鼓励将裸参考与shared_ptr混合。主要原因是它打破了引用计数概念,这是shared_ptr用于确定何时可以安全地释放引用对象的概念。任何裸引用必须的寿命比最长寿命的shared_ptr短,否则崩溃正在酝酿中。

如果要在某种容器中包含一组对象,并且仍然能够使用shared_ptr访问这些对象,最好使用shared_ptr的数组/向量/容器,因为这会带来容器“进入折叠“可以说与shared_ptr概念。这样做的好处是,当从容器中删除对象时,它不会立即被释放,反之亦然,引用它的共享指针。

使用shared_ptrs容器的缺点是它使得指针数学稍微困难一些,因为实际上,你使用了一种指向指针的指针。

答案 1 :(得分:2)

您只能以两种方式使用共享指针:

1)创建新对象时,

std::shared_ptr<something> ptr(new something);

2)将共享指针复制到另一个时。

std::shared_ptr<something> copy(ptr);

因此,如果在数组中创建一个对象,它将不适合共享指针方案。

如果要在向量中保存使用共享指针分配的对象,则必须保存其共享指针:

std::vector<std::shared_ptr<something> > my_vector;

my_vector.push_back(new something);

清除向量时,所有指针都会被清除,因此只有向量引用的对象会被删除:

my_vector.clear();  // do "delete something" as required

作为旁注,你在问题中说“效率更高”......共享指针的效率并不高,并且它们不太可能在你的软件中创建比你想象的更多的代码。

C ++中一个非常重要的例外是异常。保存在智能指针中的对象(在这种情况下unique_ptr也可以工作)将在异常时自动删除。这是非常重要的。例如:

std::shared_ptr<something> ptr(new something);

...
if(this_is_true) throw std::logic_error("something's wrong");
...

在上面的代码中,指针ptr会在throw返回之前自动删除。这不是我所说的更有效,但在清洁方面要好得多。如果你调用一个可以抛出的函数(例如另一个new),那么处理每个调用变得非常繁琐(即你需要在每次调用时都有一个try / catch,并且catch必须删除指针,然后重新抛出。)

所以从这个意义上说,它是有效率的。但是,就执行速度而言,它可能没有比不使用共享指针更快(或更慢)。

答案 2 :(得分:1)

指向子对象(数组元素或对象成员)的指针可以与指向整个对象的指针共享所有权。使用模板构造函数创建shared_ptr到子对象:

template< class Y > 
shared_ptr( const shared_ptr<Y>& r, T *ptr );

它接受任何类型的shared_ptr和指向目标类型的指针(可能完全不相关)。构造的shared_ptr与源对象r共享所有权,但当取消引用时指向ptr

例如,创建指向std::array的第三个数组元素的指针:

class foo {};
auto array = std::make_shared<std::array<foo, 16>>();
auto element_ptr = std::shared_ptr<foo>(array, array->data() + 2);

或对象的数据成员:

struct foo {
  int i;
};
auto some_foo = std::make_shared<foo>();
auto foo_member = std::shared_ptr<int>(some_foo, &some_foo->i);

遗憾的是,没有方便的类型推导“make”函数,例如std::make_shared,显然指定子对象的类型很烦人。也许我们应该写自己的:

template <typename Object, typename SubObject>
inline std::shared_ptr<SubObject>
make_sub_ptr(const std::shared_ptr<Object>& c, SubObject* ptr) {
    return {c, ptr};
}

所以我们可以将早期的创作简化为:

auto element_ptr = make_sub_ptr(array, array->data() + 2);
// ...
auto foo_member = make_sub_ptr(some_foo, &some_foo->i);

<强> Live Demo at Coliru