new和make_shared用于共享指针

时间:2014-11-22 21:39:11

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

我遇到了this帖子和@kerek SB声明的其中一个答案

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则为2 让make_shared更有效率?

  3. 对此的一点澄清将不胜感激。

4 个答案:

答案 0 :(得分:7)

在那个问题中,“第二个变量”引用了这一行:

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<Object>(new Object)的样子:

enter image description here

shared_ptrWidget*,但它与ref计数器不在同一个内存块中,因为它们是分开分配的。传入了Widget*,并且内部分配了引用计数块,这就是Widget位于单独的内存空间中的原因。

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

enter image description here

(我偷了Herb Sutter的两张照片)。我们仍然需要一个Widget和一个引用计数块,但是只需要一次调用new / malloc就可以获取整个内存块,只需要足够大,所以{{1并且ref计数块在内存中最终连续。

答案 1 :(得分:3)

  1. 被称为第二个变量的代码实际上就是这个(取自OP代码):

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

    创建std::shared_ptr,它会创建指向Object的指针。

  2. 使用带有裸指针的构造函数创建std::shared_ptr时,必须将指针传递给已分配的内存(例如,使用new分配)。这意味着在创建std::shared_ptr对象本身时已经分配了对象的内存。 std::shared_ptr需要为自己的工作分配内存,例如参考柜台。因此,有2个分配:使用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个分配,因为你传递构建对象不是所需的参数指向对象本身的指针。然后,std::make_shared可以在保存对象和ref计数器时分配内存。

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

答案 2 :(得分:2)

  

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

我相信引用是指原始海报的代码,它声称创建了一个智能指针,但实际上并没有这样做。 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需要为计数器和对象本身分配一个槽。可以一次性分配内存,然后将其中一部分用于计数器,其余部分用于对象。

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

答案 3 :(得分:1)

  1. 第二个仍然是共享指针。它正在调用shared_ptr的构造函数:
  2. http://www.cplusplus.com/reference/memory/shared_ptr/shared_ptr/

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