【问题标题】:What happens when using make_shared使用 make_shared 时会发生什么
【发布时间】:2014-09-06 22:20:46
【问题描述】:

我很感兴趣这两行代码是否相同:

shared_ptr<int> sp(new int(1)); // double allocation?
shared_ptr<int> sp(make_shared<int>(1)); // just one allocation?

如果这是真的,有人能解释一下为什么第二行只有一个分配吗?

【问题讨论】:

  • 要清楚,这不是int 的双重分配。这只是两个独立的分配:一个用于int 对象,另一个用于shared_ptr 控制块。第二行只是一次性分配int和控制块。
  • 在第二种情况下,make_shared 分配了int 和控制块,因此可以一次性分配两者。在第一种情况下,您分配了intshared_ptr 的构造函数分配了控制块,没有办法统一分配。
  • 注:不能保证make_shared 只进行一次分配,实现只是可能进行一次分配...但是如果您的实现没有执行该优化,您应该向您的供应商投诉(至少 GCC、LLVM 和 MSVC 实现可以做到这一点,boost::make_shared 也可以做到)
  • 这是对幕后情况的另一个体面解释:stackoverflow.com/a/8646062/576911

标签: c++ c++11 smart-pointers make-shared


【解决方案1】:

第一种情况不执行双重分配,它执行两次分配,一个用于托管对象,一个用于shared_ptr控制块

对于第二种情况,cppreference 很好地解释了为什么 std::make_shared通常只执行它所说的一次内存分配(强调我的未来):

此函数通常为 T 对象和 shared_ptr 的控制块,具有单个内存分配(它是 标准中的非约束性要求)。相比之下,声明 std::shared_ptr p(new T(Args...)) 执行至少两个内存 分配,这可能会产生不必要的开销。

std::shared_ptr 部分它说:

当 shared_ptr 通过调用 std::make_shared 或 std::allocate_shared,控制块和 托管对象是使用单个分配创建的。被管理对象 在控制块的数据成员中就地构造。什么时候 shared_ptr 是通过 shared_ptr 构造函数之一创建的,即 管理对象和控制块必须分开分配。在 在这种情况下,控制块存储一个指向托管对象的指针。

make_shared 描述与C++11 draft standard 部分中的20.7.2.2.6 描述一致shared_ptr 创建

template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args);
template<class T, class A, class... Args>
  shared_ptr<T> allocate_shared(const A& a, Args&&... args);

[...]

备注:实现应该执行不超过一个内存 分配。 [注:这提供了等效于 侵入式智能指针。 ——尾注]

[ 注意:这些函数通常会分配比 sizeof(T) 以允许内部簿记结构,例如 参考计数。 ——尾注]

Herb Sutter 对在GotW #89 Solution: Smart Pointers 中使用make_shared 的优势进行了更详细的说明,并指出了一些优势:

  • 它减少了分配开销
  • 它改善了局部性。
  • 避免显式的 new。
  • 避免异常安全问题。

请注意,在使用 std::weak_ptr using make_shared has some disadvantages 时。

【讨论】:

    【解决方案2】:

    cppreference std::shared_ptrImplementation notes 部分的解释

    在典型的实现中,std::shared_ptr 只保存两个指针:

    1. 指向托管对象的指针
    2. 指向控制块的指针

    当 shared_ptr 通过调用 std::make_shared 或 std::allocate_shared,控制块和 托管对象是使用单个分配创建的。被管理对象 在控制块的数据成员中就地构造。什么时候 shared_ptr 是通过 shared_ptr 构造函数之一创建的,即 管理对象和控制块必须分开分配。在 在这种情况下,控制块存储一个指向托管对象的指针。

    【讨论】:

      【解决方案3】:

      还有一个潜在的微妙错误:在sp(new int) 中,您首先分配一个int(其指针指向sp),而sp 本身必须分配一个控制块(将包含计数器和删除器)。

      现在,如果最后一次分配sp 失败(内存不足),您将留下一个分配的 int 堆,其指针不被任何人持有,因此无法删除。 (内存泄漏)。

      【讨论】:

        猜你喜欢
        • 2012-04-22
        • 2020-08-14
        • 1970-01-01
        • 1970-01-01
        • 2010-09-26
        • 2010-12-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多