复制或constref一个shared_ptr?

时间:2019-04-25 20:58:15

标签: c++ performance shared-ptr dereference

请考虑一些包含_sku(例如struct成员)的数据源。如果您保证它不是一个临时值,但在当前范围内将是有效的,并且您希望对该指针使用别名:哪个性能更好,请复制shared_ptr或使用(可能是const)参考原著吗?

示例:

shared_ptr

编辑:动机。例如,当1)获取struct S { shared_ptr<T> ptr; }; void fun(S s) { shared_ptr<T> alias1 = s.ptr; shared_ptr<T> const& alias2 = s.ptr; /* do something with the alias */ } 涉及遍历一连串的函数调用或derefs或2)希望提高可读性。

权衡。从某种意义上说,复制指针看起来更“正确”,因为它模仿了您对原始指针的处理方式,但是它需要refcount机制来完成其工作(在构造和销毁方面) )。另一方面,拥有一个您可能经常使用的引用会导致额外的取消引用,这反过来又会很昂贵(该引用是您对保存的s.ptr对象所做的任何事情的基础)。

有一个通用的经验法则是什么更有效?

2 个答案:

答案 0 :(得分:2)

经验法则

在不拥有所有权的情况下修改或操作数据的算法应作用于对数据(或其容器)的迭代器或引用。

例如,如果要查找一堆值的平均值,则应编写一个将迭代器带到值集的函数(或通过const引用获取容器)。您不应该要做的是通过副本传递矢量,或传递智能指针。

复制shared_ptr会产生开销,算法本身应该始终希望它们处理的数据超过计算的寿命。

何时使用shared_ptr shared_ptr在编写容器或其他需要在一段未知时间内保留一条数据的类时使用。对象拥有所有权,而不是功能,而不是算法。 (当然,成员函数可以接受shared_ptr作为输入,但是只有在它们所需要的类不需要这些数据的情况下,这样做才有意义)。

线程如何?

线程是该经验法则的例外。如果要启动新线程,则绝对应该按值传递任何shared_ptr。该线程可能存在一段未知的时间,您需要确保它有权访问的任何数据在线程退出之前一直有效。

复制shared_ptr比开始启动线程要便宜得多,因此开销很小。

答案 1 :(得分:0)

高级Perez关于使用共享指针的最佳方法是正确的。

如果完全使用它们,则应明智地使用它们,以增加 在大多数编译器中,引用计数是一项非常昂贵的工作。

在我开发的一款引人注目的游戏中,大部分帧时间都占用了一个共享指针取消引用。

在位掩码给出的示例中,结构本身是通过值传递的,由于复制删除或类似的优化,它显然不如听起来那样糟糕。

What are copy elision and return value optimization?

如果不幸遇到了使用共享指针的麻烦,则可以使用get()将其转换为常规指针。

当然,这会使代码的安全性降低,但是谁想要无聊的生活:)

#include <memory>

struct S {
  std::shared_ptr<int> ptr;
};

void fun(S s) {
    int *alias = s.ptr.get();
    *alias = 1;
}

Godbolt:

fun(S):                               # @fun(S)
    mov     rax, qword ptr [rdi]
    mov     dword ptr [rax], 1
    ret