【问题标题】:How to pass and share shared_ptr ownership with a better performance? [duplicate]如何以更好的性能传递和共享 shared_ptr 所有权? [复制]
【发布时间】:2018-09-06 10:59:00
【问题描述】:

例如

class A
{
public:
    // Option 1
    void setI_1(shared_ptr<int> i) { m_i = i; }

    // Option 2
    void setI_2(shared_ptr<int> i) { m_i = move(i); }

    // Option 3
    void setI_3(shared_ptr<int> const& i) { m_i = i; }

private:
    shared_ptr<int> m_i;        
};

通常哪个选项更好?

我在 Visual Studio 2017 上进行基准测试。选项 2 在我测试的所有情况下都为我提供了最佳性能。在某些情况下,选项 3 与选项 2 具有相似的性能,但有些情况更糟。谢谢!

【问题讨论】:

  • 对其进行基准测试。如果这取决于编译器,我不会感到惊讶(一些旧的编译器在按值传递和 const 引用方面存在怪癖),但是,对于现代编译器,如果它们都相等,我不会感到惊讶。跨度>
  • 选项 3 是最好的。如果需要,您仍然可以检查生成的代码。
  • 不出所料,使用 clang,当为“选项 2 和 3”使用左值参考时,我得到了最好的程序集,但是,使用纯右值,选项 2 是最好的。显然,里程可能会有所不同。使用选项 3。
  • @MM:您链接的答案特定于将其传递给函数的情况(没有迹象表明将制作持久副本),并且(至少根据 cmets)正在避免在“存储副本”的情况下处理按值传递,后跟 std::move(此处为选项 2),因为它依赖于发布时并不总是可用的 C++11 功能。鉴于 C++11 在这一点上无处不在,并且这个问题专门将副本存储给成员,而不仅仅是临时使用,我认为您的链接答案不适用。

标签: c++ c++11 shared-ptr


【解决方案1】:

在您的情况下,选项 2 是最好的情况。你可能想移动一个指向类的指针,所以当你不再需要它时,你不需要一次又一次地增加和减少引用计数器:

{
    shared_ptr<int> ptr = std::make_shared<int>(42);

    A a;
    a.setI_2(std::move(ptr)); // I no longer need ptr, so I can move it
};

指针移动到参数列表并且ptr 为空/使用引用ptr 的右值引用初始化参数,因此ptr 为空。然后它从参数列表移动到成员并且参数为空。你看,没有(需要)副本,这意味着引用计数器的更改。

我更喜欢T const&amp; 仅用于仅从引用读取且不用于初始化另一个变量的函数。如果在示例中使用了带引用的第三个选项,则需要复制指针,因为 setI_3 的主体在赋值语句中有一个副本(不移动)。

第一个选项显然是最糟糕的,因为指针被复制了多次。

当您将shared_ptrunique_ptr 交换时,编译器会强制您使用最快的方式——移动。

我会添加第四个选项:

// Option 4
void setI_4(shared_ptr<int>&& i) { m_i = move(i); }

请注意,即使i 是一个右值引用,在代码中使用时它也不是一个右值引用,因此在没有移动时会被复制;

第四个选项仅与第二个选项不同,传递的参数始终需要是一个右值引用,而在第二个中,i 本身是一个指针变量,在第四个中@987654330 @ 是一个引用(对函数体可以做任何事情的东西,但它存在于函数之外(std::move 允许这样做))。参数变量是从调用者的参数元组中的表达式构造的,shared_ptr 的构造函数如果不是右值引用,则自动复制指针(这意味着引用计数器增加)。

setI_2 的调用如下:

{
    shared_ptr<int> ptr = std::make_shared<int>(42);
    /// ptr owns 42
    A a;
    {   // a.setI_2(std::move(ptr));
        // initializing the parameter
        shared_ptr<int> i{std::move(ptr)};
        // now i owns 42 and ptr no longer owns it and points to null
        {   // the body of the function
            a.m_i = std::move(i);
            // now a.m_i owns 42 and i no longer owns it and points to null
        };
    };
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-25
    • 2016-12-11
    • 2012-03-07
    • 2016-01-30
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 2013-02-15
    相关资源
    最近更新 更多