make_shared"证据"与默认构造

fen*_*fen 2 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
Run Code Online (Sandbox Code Playgroud)

调试时我在本地查看这样的东西(删除了一些行)

-   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 *
Run Code Online (Sandbox Code Playgroud)

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

在第二个版本中,我们有0x01208d90为参考计数器,0x01203c50的对象.那些地方相当遥远.

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

Set*_*gie 5

如果您阅读了cppreference的页面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.


And*_*owl 4

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

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

在 的情况下sp,您可以通过单独分配对象new Test(),然后构造该shared_ptr对象。的构造函数shared_ptr必须为引用计数器发出新的分配。因此,指向对象的地址和引用计数器的地址是遥远的。