【问题标题】:C++ shared pointers referencing an object within an array引用数组中对象的 C++ 共享指针
【发布时间】:2014-07-23 20:35:34
【问题描述】:

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

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

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

【问题讨论】:

  • 你应该举一个例子来说明你的意思是“在一个数组/向量中”。但是,是的,如果所有 shared_ptrs 都消失了,它就会被销毁。
  • 是的,使用arrayvectorshared_ptrs。
  • shared_ptr 无法知道您的数组中有一个副本。
  • 共享指针都是关于对象的所有权。如果一个对象是另一个对象的一部分,那么它就归另一个对象所有。拥有第二个实体也试图拥有它是没有意义的。
  • @Incubbus 是和否。 weak_ptr 不会增加引用计数,但它也被设计为在它指向的对象被释放时允许正常失败。它不会阻止对象被释放,所以你仍然需要在使用它之前检查它是否有效。

标签: c++ arrays pointers smart-pointers


【解决方案1】:

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

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

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

【讨论】:

  • 那么您是在传递 shared_ptr 副本或引用吗?如果 shared_ptr 对象很多,它如何进行引用计数?
  • 谢谢,这是我一直在寻找的问题,一直在尝试研究如何实现智能指针以防止可能导致资源泄漏的异常问题,并且在某种程度上已经得到在我完全掌握它们之前有点忘乎所以
  • @snowandotherjoys 实现各不相同,但 shared_ptr 对象通常使用静态对象来跟踪实例之间的引用计数。
【解决方案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必须删除指针然后重新抛出。)

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

【讨论】:

    【解决方案3】:

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

    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);
    

    很遗憾没有像std::make_shared 这样方便的类型推断“make”函数,显式指定子对象的类型很烦人。也许我们应该自己写:

    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

    【讨论】:

      猜你喜欢
      • 2017-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多