【问题标题】:new and make_shared for shared pointersnew 和 make_shared 用于共享指针
【发布时间】:2014-11-22 21:39:10
【问题描述】:

我遇到了this 的帖子和@kerek SB states 的答案之一

std::shared_ptr<Object> p1 = std::make_shared<Object>("foo");
std::shared_ptr<Object> p2(new Object("foo"));

在您的代码中,第二个变量只是一个裸指针,而不是 完全共享指针。

现在是肉。 make_shared (在实践中)更有效,因为 它分配参考控制块与实际 一次动态分配中的对象。相比之下,构造函数 对于接受裸对象指针的 shared_ptr 必须分配另一个 引用计数的动态变量。权衡是 make_shared(或其表亲 allocate_shared)不允许您 指定一个自定义删除器,因为分配是由 分配器。

(这不影响对象本身的构造。从 对象的视角这两个版本没有区别。 更有效的是共享指针本身,而不是托管 对象。)

现在我有两个关于这篇文章的问题,如果有人能澄清这一点,我将不胜感激

  1. 为什么第二个不是共享指针?这不会增加引用计数吗

  2. make_shared 如何只分配一个内存而 new 分配两个因此 让 make_shared 更高效?

我们将不胜感激。

【问题讨论】:

  • new Object("foo") 首先被独立评估,创建一个不知道它将由共享指针管理的块,因此没有引用计数的大小。跨度>
  • 希望有人 ping Kerrek

标签: c++ pointers shared-ptr make-shared


【解决方案1】:

在那个问题中,“第二个变量”指的是这一行:

auto ptr_res2(new Object("new")); // this creates an Object*

不是这个:

std::shared_ptr<Object> p2(new Object("foo")); // this creates a shared_ptr<Object>

为什么make_shared 使用一种分配更有效的最好解释是比较图像。这是std_shared_ptr&lt;Object&gt;(new Object) 的样子:

shared_ptr 有一个Widget*,但它与 ref 计数器不在同一个内存块中,因为它们是单独分配的。 Widget* 被传入,ref 计数块是在内部分配的,这就是为什么Widget 在单独的内存空间中。

另一方面,这是一个分配的样子:

(我从 Herb Sutter 那里偷了两张照片)。我们仍然需要一个Widget 和一个引用计数块,但是整个内存块在一次调用new / malloc 时被抓取,大小刚好足够,所以Widget 和引用计数块结束在内存中连续。

【讨论】:

  • 对每张图片做一点解释会很棒
  • @MistyD 澄清了一下。
  • @Barry:H/T 用于清除关于“第二个变量”的那一点 - 我没有查看 OP 发布的原始链接,并且正在为这不是一个共享变量。
  • @Deduplicator 我认为回答第二个问题是这个问题中的“主要”问题。
  • 感谢 Barry 的帮助!!
【解决方案2】:
  1. 称为第二个变量的代码实际上是这样的(取自OP的代码):

    auto ptr_res2(new Object("new"));
    

    创建了std::shared_ptr,它创建了一个指向Object的指针。

  2. 当使用带有裸指针的构造函数创建std::shared_ptr 时,您必须将指针传递给已分配的内存(例如,使用new 分配的内存)。这意味着在创建std::shared_ptr 对象本身时已经为该对象分配了内存。 std::shared_ptr 需要为其自己的工作分配内存,例如参考计数器。所以有两种分配:一种使用new 传递给std::shared_ptr 的ctor,另一种是在构造std::shared_ptr 本身时需要的。

    Object* ptr = new Object{"Foo"}; // Allocation 1 (object).
    std::shared_ptr<Object> p1{ptr}; // Allocation 2 (internal counters).
    

    帮助函数std::make_shared 仅使用 1 次分配,因为您将构造 object 所需的参数 传递给它,不是 指向对象本身。然后std::make_shared 可以分配一次同时保存对象和引用计数器的内存。

    auto p2 = std::make_shared<Object>{"Foo"} // Allocation 1 (object & counter).
    

【讨论】:

    【解决方案3】:

    为什么第二个不是共享指针?这不会增加引用计数吗

    我相信引用是指原始发布者的代码,它声称创建一个智能指针,但实际上并没有这样做。 ptr_res2 只是一个普通的指针。

    cout << "Create smart_ptr using new..." << endl;
    auto ptr_res2(new Object("new"));
    cout << "Create smart_ptr using new: done." << endl;
    

    make_shared 如何只分配一次内存,new 分配两次,从而使 make_shared 更高效

    make_shared 需要为计数器和对象本身分配一个槽。可以一次性分配内存,然后将其中的一部分用于计数器,将其余部分用于对象。

    请注意,这也意味着计数器和对象本身在内存中彼此相邻,从而提高了数据局部性。

    【讨论】:

      【解决方案4】:
      1. 第二个仍然是共享指针。它正在调用 shared_ptr 的构造函数:

      http://www.cplusplus.com/reference/memory/shared_ptr/shared_ptr/

      1. 但是,使用 make_shared 更有效,因为它只进行一次分配而不是两次分配。请记住,shared_ptr 需要堆上的空间供 Object 使用,并且管理器还需要跟踪共享指针和弱指针的数量。如果您使用构造函数,则为 Object 分配空间,然后将指针传递给需要为管理器分配空间的构造函数。相反,如果您使用 make_shared,它会分配一块存储 Object 和 manager 的内存。由于分配相对昂贵,因此一次分配优于两次。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-01-28
        • 1970-01-01
        • 1970-01-01
        • 2016-11-16
        • 1970-01-01
        • 2012-01-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多