【问题标题】:Passing std::shared_ptr to Constructors将 std::shared_ptr 传递给构造函数
【发布时间】:2012-08-13 16:57:36
【问题描述】:

在创建 Stuff 并赋予 Foo 所有权方面,以下是合理且有效的方法吗?

class Foo
{
    explicit Foo(const std::shared_ptr<Stuff>& myStuff)
        : m_myStuff(myStuff)
    {
    }

    ...

private:
    const std::shared_ptr<Stuff> m_myStuff;
}

std::shared_ptr<Stuff> foosStuff(new Stuff());
Foo f(foosStuff);

【问题讨论】:

  • 不是,Foo取得所有权。 shared_ptr 的意义在于分享所有权。
  • 或者更喜欢std::shared_ptr&lt;Stuff&gt; foosStuff(new Stuff());
  • @juanchopanza 在这种情况下,您可能更希望看到 unique_ptr?
  • @Baz 如果你真的想拥有所有权,那么可以。

标签: c++ std


【解决方案1】:

既然您对效率感兴趣,我想说明两点:

shared_ptr 是移动构造比复制构造便宜的许多标准库类型之一。复制构造 shared_ptr 比较慢,因为复制需要引用计数器自动递增,而移动 shared_ptr 根本不需要接触引用的数据或计数器。从 Dave Abrahams 的文章“Want Speed? Pass by value!”中可以了解到,在某些情况下,按值获取函数参数实际上是有益的。这是以下情况之一:

class Foo
{
  explicit Foo(std::shared_ptr<Stuff> myStuff)
  : m_myStuff(move(myStuff))
  {}

  ...

private:
  std::shared_ptr<Stuff> m_myStuff;
};

现在你可以写了

Foo f (std::make_shared<Stuff>());

其中的参数是一个临时的,并且没有 shared_ptr 被复制(只是移动一次或两次)。

这里使用 std::make_shared 的好处是只完成了一次分配。在您的情况下,您自己分配了 Stuff 对象,并且 shared_ptr 构造函数也必须动态分配引用计数器和删除器。 make_shared 只需一次分配即可为您完成所有工作。

【讨论】:

  • 我喜欢 make_shared 功能
  • 顺便说一句:如果您更改为 std::unique_ptr 您实际上 HAVE 通过值传递它;)
  • @sellibitze 如果您不打算保留它,那么在其构造函数中向类传递 shared_ptr 有什么好处。当然,在这种情况下,最好传入一个 unique_ptr 并让类从中构造它的 shared_ptr 吗?
  • @jleahy:“好处”是构造函数仍然接受 shared_ptr,同时不涉及复制成本。当然,在这个使用示例中,我可以使用 unique_ptr 代替。但这可能会不必要地限制接口。如果你要存储一个shared_ptr,只接受一个shared_ptr 作为参数,而不是一个unique_ptr。存储 shared_ptr 是否是一个好主意是另一个问题,如果不知道 Baz 打算在这里做什么,就不能简单地回答。
  • @sellibitze 阅读您链接到的文章后,我现在明白了。如果您正在构建的 shared_ptr 是临时的,那么编译器将能够完全删除副本。
【解决方案2】:

是的,这完全合理。这样,管理共享指针所涉及的工作只需要完成一次,如果您通过值传递它,则不需要两次。您还可以考虑使用 make_shared 来避免复制构造。

std::shared_ptr<Stuff> foosStuff(std::make_shared<Stuff>());

您可以做出的唯一改进是,如果 Foo 是唯一的所有者(即,您不会在创建 Foo 后保留 foosStuff),那么您可以切换到使用 std::unique_ptr 或 boost::scoped_ptr (这是 C++11 之前的等价物),开销会更少。

【讨论】:

  • 好的,我应该学习更多关于其他智能指针的知识,因为我只真正熟悉 shared_ptr。
  • 什么是scoped_ptr,它不是 C++ 的一部分?也许你的意思是std::unique_ptr?为什么要自定义删除器?
  • @ChristianRau:我想boost::scope_ptr 是什么意思。删除位可能是对早期(隐蔽)编辑的过时引用?
  • @KerrekSB 我也想,但是假设并不能帮助任何阅读答案的人理解在 C++ 标准库中有一个非常适合的所有权获取指针,而无需使用 boost(这是'甚至没有提到)。问题已经改变了。一旦他注意到问题的变化和不准确的信息,对他的答案的反对票就会神奇地消失。
  • @ChristianRau 由于对问题的更改,我的回答有些多余。如果不是自定义删除器,我也会更喜欢 make_shared,就像在 KerrebSB 的回答中一样。我已经更新了它以防止混淆。
【解决方案3】:

拥有make_foo 助手可能更有效:

Foo make_foo() { return Foo(std::make_shared<Stuff>()); }

现在你可以说auto f = make_foo();。或者至少自己使用make_shared 调用,因为生成的shared_ptr 可能比由new 表达式构造的更有效。如果Stuff 实际上接受构造函数参数,那么私有辅助构造函数可能是合适的:

struct Foo
{
    template <typename ...Args>
    static Foo make(Args &&... args)
    {
        return Foo(direct_construct(), std::forward<Args>(args)...);
    };

private:

    struct direct_construct{};

    template <typeaname ...Args>
    Foo(direct_construct, Args &&... args)
    : m_myStuff(std::make_shared<Stuff>(std::forward<Args>(args)...))  // #1
    {  }
};

你既可以将Foo::make包装成上面的make_foo,也可以直接使用:

auto f = Foo::make(true, 'x', Blue);

也就是说,除非您真的共享所有权,否则std::unique_ptr&lt;Stuff&gt; 听起来是更可取的方法:它在概念上更简单,也更有效。在这种情况下,您会在标记为#1 的行中输入m_myStuff(new Stuff(std::forward&lt;Args&gt;(args)...))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-22
    • 1970-01-01
    • 2021-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多