make_shared“证据”与默认构造

时间:2013-02-02 20:03:23

标签: c++ memory-management smart-pointers

make_shared为对象和引用计数器分配单个块。因此使用这种技术有明显的性能优势。

我在VS2012做了简单的实验,我一直在寻找'证据':

std::shared_ptr<Test> sp2 = std::make_shared<Test>();
std::shared_ptr<Test> sp(new Test());
// Test is a simple class with int 'm_value' member

调试时我在本地视图中查看类似的内容(删除了一些行)

-   sp2 shared_ptr {m_value=0 }  [make_shared] std::shared_ptr<Test>
+   _Ptr    0x01208dec {m_value=0 } Test *
+   _Rep    0x01208de0 make_shared  std::_Ref_count_base *

-   sp  shared_ptr {m_value=0 } [default] std::shared_ptr<Test>
+   _Ptr    0x01203c50 {m_value=0 } Test *
+   _Rep    0x01208d90 default  std::_Ref_count_base *

似乎 sp2 分配在 0x01208de0 (有一个ref计数器),然后在 0x01208dec 中有一个Test对象。地点非常接近。

在第二个版本中,我们对于ref计数器有 0x01208d90 ,对于对象有 0x01203c50 。那些地方相当遥远。

这是正确的输出吗?我能理解这一点吗?

3 个答案:

答案 0 :(得分:5)

如果您阅读cppreference's page for make_shared,他们会说:

  

此函数使用单个内存分配为T对象和shared_ptr的控制块分配内存。相反,声明std::shared_ptr<T> p(new T(Args...))执行两次内存分配,这可能会产生不必要的开销。

所以这是预期的行为,你正确地解释了它。

当然,这是有道理的; shared_ptr如何控制已分配的对象的分配?使用make_shared,你可以让它负责分配对象,这样它就可以在你想要的任何地方分配空间,就在柜台旁边。

附录:正如Pete Becker在评论中指出的那样,标准的第20.7.2.2.6 / 6条规定,鼓励但不要求实施只执行一次分配。所以你不应该依赖你所观察到的这种行为,尽管如果你总是使用make_shared,你可以安全地说你没有什么可失去的东西可以获得。

答案 1 :(得分:1)

是的,显示的输出是正确的。

在通过sp2创建的make_shared<>()的情况下,有一个连续内存块包含引用计数器和分配的对象。这就是两个地址接近的原因,这也是make_shared<>()存在的主要原因之一(仅执行一个分配而不是两个)。

sp的情况下,您可以通过new Test()单独分配对象,然后构造shared_ptr对象。 shared_ptr的构造函数必须为引用计数器发出新的分配。因此,尖头物体的地址和参考计数器的地址很远。

答案 2 :(得分:0)

  

这是正确的输出吗?

看起来像。

std::make_shared的重点是性能 - 动态内存分配相对昂贵,仅仅为了保存引用计数器而进行额外分配可能相当浪费。因此,std::make_shared对象和计数器分配足够大的内存块,然后在该板中正确的位置初始化对象(使用placement new