4

虽然我花了一段时间才习惯它,但我现在养成了让我的函数通过左值引用const而不是值来获取共享指针参数的习惯(当然,除非我需要修改原始参数,在这种情况下我通过对非的左值引用来获取它们const):

void foo(std::shared_ptr<widget> const& pWidget)
//                               ^^^^^^
{
    // work with pWidget...
}

这样做的好处是避免了不必要的共享指针副本,这意味着线程安全地增加了引用计数并可能导致不希望的开销。

现在我一直在想,采用某种对称的习惯来检索函数返回的共享指针是否明智,就像在以下代码片段的末尾一样:

struct X
{
    // ...
    std::shared_ptr<Widget> bar() const
    {
        // ...
        return pWidget;
    }
    // ...
    std::shared_ptr<Widget> pWidget;
};

// ...

// X x;
std::share_ptr<Widget> const& pWidget = x.bar();
//                     ^^^^^^

采用这种编码习惯有什么陷阱吗?一般来说,我有什么理由更喜欢将返回的共享指针分配给另一个共享指针对象而不是将其绑定到引用?

4

3 回答 3

6

这只是对捕获对临时对象的 const 引用是否比创建副本更有效的老问题的重制。简单的答案是它不是。在行中:

// std::shared_ptr<Widget> bar();
std::shared_ptr<Widget> const & pWidget = bar();

编译器需要创建一个本地未命名变量(不是临时变量),通过调用来初始化它,bar()然后将引用绑定到它:

std::shared_ptr<Widget> __tmp = bar();
std::shared_ptr<Widget> const & pWidget = __tmp;

在大多数情况下,它会避免创建引用,而只是在函数的其余部分中为原始对象设置别名,但在一天结束时,无论是调用变量pWidget还是使用__tmp别名都不会带来任何好处。

相反,对于普通读者来说,它可能看起来bar()并没有创建一个对象,而是产生了一个已经存在的引用std::shared_ptr<Widget>,因此维护者将不得不寻找bar()定义的位置以了解是否pWidget可以在 this 范围之外进行更改功能。

通过绑定到 const 引用来延长生命周期是该语言中的一个奇怪的特性,它几乎没有实际用途(即当引用是基数并且您不太关心返回的确切派生类型是什么时,即 ie ScopedGuard)。

于 2013-05-01T14:22:17.443 回答
3

您可能会向后优化:

struct X
{
    // ...
    std::shared_ptr<Widget> const& bar() const
    {
        // ...
        return pWidget;
    }
    // ...
    std::shared_ptr<Widget> pWidget;
};

// ...

// X x;
std::share_ptr<Widget>  pWidget = x.bar();

bar返回成员变量一样,它必须shared_ptr在您的版本中获取副本。如果通过引用返回成员变量,则可以避免复制。

这在您的原始版本和上面显示的版本中都无关紧要,但如果您调用:

x.bar()->baz()

在您的版本shared_ptr中,将创建一个新的,然后调用 baz。

在我的版本中, baz 直接在 的成员副本上调用shared_ptr,并且避免了原子引用递增/递减。

当然,shared_ptr复制构造函数(原子增量)的成本非常小,除了对性能最敏感的应用程序之外,甚至在所有应用程序中都不会引起注意。如果您正在编写一个性能敏感的应用程序,那么更好的选择是使用内存池架构手动管理内存,然后(小心地)使用原始指针。

于 2013-05-01T14:39:41.273 回答
2

除了David Rodríguez - dribeas 所说的之外,绑定到const引用并不能让您免于复制,并且计数器无论如何都会递增,以下代码说明了这一点:

#include <memory>
#include <cassert>

struct X {
    std::shared_ptr<int> p;
    X() : p{new int} {}
    std::shared_ptr<int> bar() { return p; }
};

int main() {
    X x;
    assert(x.p.use_count() == 1);
    std::shared_ptr<int> const & p = x.bar();
    assert(x.p.use_count() == 2);
    return 0;
}
于 2013-05-01T15:27:36.923 回答