8

这更像是一个样式而不是性能问题。我刚刚将(大部分)我的指针转换为 shared_ptr 对象,并且不情愿地接受了weak_ptrs 作为原始指针的替代品。我的问题是,迭代共享指针对象的序列(比方说向量)的首选方法是什么?这是我一直在做的事情:

std::vector<std::shared_ptr<A>> my_sequence;
// Do something to fill my_sequence;

for (std::shared_ptr<A> const& ptr : my_sequence)
{
  ptr->AMethod();
}

这违反了 *don't use shared_ptr references* 规则,那么有什么好的选择,为什么?

我要问的问题是;该技术是否稳健,即。对于 AMethod() 超小和 my_sequence 超大,此方法是否会由于 shared_ptr 副本而开始不必要地阻碍性能?它可读吗?简单吗?

4

2 回答 2

8

不使用 shared_ptr 引用的原因是因为您破坏了它试图保护自己免受伤害的机制。即有一个潜在的悬空指针。如果您正在遍历容器,这应该不是问题,因为对象不会意外地消失在您身上。您只是不应该存储您以后可能会使用的 shared_ptr 引用。

即这很糟糕:

struct Y
{
    int y;
    Y() : y(1) {}
};

struct X
{
     shared_ptr<Y>& ref_shared_ptr_y;
     X(shared_ptr<Y>& y) : ref_shared_ptr_y(y) {}
};

void main()
{
    shared_ptr<Y> shared_ptr_y(new Y());
    X x(shared_ptr_y);
    y.reset();
    x.ref_shared_ptr_y->y;  // UB since x.ref_shared_ptr_y was deleted
}

shared_ptr仅当您确实需要在 2 个或更多位置之间共享对象所有权时才应使用。否则会导致不必要的开销,并表明您还没有真正考虑过所有者关系。如果您的所有权仅限于一个位置,请使用unique_ptr.

于 2013-06-24T23:57:17.730 回答
8

首先,免责声明:

shared_ptr不是灵丹妙药。它应该在实际共享所有权时使用。非共享所有权由(原始)引用表示unique_ptr,非所有权由(原始)引用表示。weak_ptrshared_ptr必须遵守但不拥有的……但通常不是针对陈旧指针的良好防御做法。默认为shared_ptr直接去最小公分母;这是糟糕的编程习惯。


这里的选择是在shared_ptr< T >, shared_ptr< T const >, shared_ptr< T > const &, 和T const &, 和之间T &。假设您没有更改任何内容,但序列可以更改其对象,因此const是可取的。这将其缩小到shared_ptr< T const >,shared_ptr< T > const &T const &

让我们将问题重新表述为,

永远shared_ptr< T > const &是正确的选择吗?

有鉴于此,评估备选方案。

  • shared_ptr< T const >(按值传递共享指针)

    • +:直接的值语义;可以通过shared_ptr< T >
    • +: 可复制或moved 扩大所有权池
    • +:将正确性传播const到被调用的函数
    • +:访问对象很快:包含直接指针参数
    • –:传递是昂贵的:复制两个机器字并原子接触一个引用计数
    • –:被调用函数关注所有权语义
  • shared_ptr< T > const &(通过引用传递共享指针)

    • +:传递与直接对象引用一样便宜
    • +:可复制以扩大所有权池
    • const–:对被调用函数失去正确性

      • 如果您shared_ptr< T const > const &改为尝试,您将传递一个临时副本,最终会遇到这种替代方法和按值传递的缺点。
    • –:对对象的访问通过额外的间接方式

    • –:被调用函数关注所有权语义
  • T const &(直接参考)

    • +:保持const正确性
    • +:调用者不关心所有权语义
    • +:快速通过,只需一个机器字
    • +:快速访问函数的直接指针参数(即使这样)
    • –:不能授予所有权

权衡一下,如果所有权没有被扩展(例如通过返回一个保留参数的对象),你真的应该传递一个简单的引用。在这种情况下,函数应该很少关心它的参数是如何拥有的。拥有它需要以shared_ptr最糟糕的方式违反关注点分离。您不能在拥有本地对象或成员子对象的同时仍然使用该函数。最糟糕的是,一切都变得更加复杂。

如果所有权可能扩展到其他人,这将是首先使用的理由shared_ptr,那么您应该始终按shared_ptr< [const] T >价值传递。是的,它确实复制了shared_ptr待命。但是复制 a 的昂贵部分shared_ptr是更新引用计数,而不是将指针压入堆栈。如果您授予所有权,则无论如何您都希望更新引用计数。取传递的值和move它,它不会触及引用计数。这样做后,您将没有任何优势和很多劣势来传递引用。

于 2013-06-28T01:10:43.513 回答