命名空间
变体
操作

std::make_shared, std::make_shared_for_overwrite

来自 cppreference.cn
< cpp‎ | memory‎ | shared ptr
 
 
内存管理库
(仅为说明目的*)
未初始化内存算法
(C++17)
(C++17)
(C++17)
受约束的未初始化
内存算法
C 库

分配器
内存资源
垃圾回收支持
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
未初始化存储
(直到 C++20*)
(直到 C++20*)
显式生命周期管理
 
 
定义于头文件 <memory>
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
(1) (自 C++11 起)
template< class T >
shared_ptr<T> make_shared( std::size_t N );
(2) (自 C++20 起)
template< class T >
shared_ptr<T> make_shared();
(3) (自 C++20 起)
template< class T >
shared_ptr<T> make_shared( std::size_t N, const std::remove_extent_t<T>& u );
(4) (自 C++20 起)
template< class T >
shared_ptr<T> make_shared( const std::remove_extent_t<T>& u );
(5) (自 C++20 起)
template< class T >
shared_ptr<T> make_shared_for_overwrite();
(6) (自 C++20 起)
template< class T >
shared_ptr<T> make_shared_for_overwrite( std::size_t N );
(7) (自 C++20 起)

为对象分配内存并使用提供的参数初始化对象。返回一个 std::shared_ptr 对象,该对象管理新创建的对象。

1) 对象的类型为 T,其构造方式如同 ::new (pv) T(std::forward<Args>(args)...),其中 pv 是一个 void* 指针,指向适合容纳 T 类型对象的存储空间。如果对象将被销毁,则销毁方式如同 pt->~T(),其中 pt 是指向该 T 类型对象的指针。

此重载仅在 T 不是数组类型时参与重载解析。

(自 C++20 起)
2) 对象的类型为 std::remove_extent_t<T>[N]。每个元素都有默认初始值。
此重载仅在 T 是无界数组类型时参与重载解析。
3) 对象的类型为 T。每个元素都有默认初始值。
此重载仅在 T 是有界数组类型时参与重载解析。
4) 对象的类型为 std::remove_extent_t<T>[N]。每个元素都具有初始值 u
此重载仅在 T 是无界数组类型时参与重载解析。
5) 对象的类型为 T。每个元素都具有初始值 u
此重载仅在 T 是有界数组类型时参与重载解析。
6) 对象的类型为 T
  • 如果 T 不是数组类型,则对象的构造方式如同 ::new (pv) T,其中 pv 是一个 void* 指针,指向适合容纳 T 类型对象的存储空间。如果对象将被销毁,则销毁方式如同 pt->~T(),其中 pt 是指向该 T 类型对象的指针。
  • 如果 T 是有界数组类型,则每个元素的初始值未指定。
此重载仅在 T 不是数组类型或是有界数组类型时参与重载解析。
7) 对象的类型为 std::remove_extent_t<T>[N]。每个元素的初始值未指定。
此重载仅在 T 是无界数组类型时参与重载解析。

目录

初始化和销毁数组元素

U 类型的数组元素按其地址升序初始化。

  • 如果 U 不是数组类型,则每个元素的构造方式如同以下表达式,其中 pv 是一个 void* 指针,指向适合容纳 U 类型对象的存储空间
2,3) ::new (pv) U()
4,5) ::new (pv) U(u)
6,7) ::new (pv) U
  • 否则,递归地初始化每个元素的元素。对于下一个维度

当返回的 std::shared_ptr 管理的对象的生命周期结束时,或者当数组元素的初始化抛出异常时,已初始化的元素将按其原始构造的相反顺序销毁。

对于要销毁的非数组类型 U 的每个数组元素,其销毁方式如同 pu->~U(),其中 pu 是指向该 U 类型数组元素的指针。

(自 C++20 起)

[编辑] 参数

args - 将用于构造 T 类型对象的参数列表
N - 要使用的数组大小
u - 用于初始化数组中每个元素的初始值

[编辑] 返回值

指向 T 类型对象的 std::shared_ptrstd::remove_extent_t<T>[N](如果 T 是无界数组类型)(自 C++20 起)

对于返回的 std::shared_ptr rr.get() 返回一个非空指针,并且 r.use_count() 返回 1

[编辑] 异常

可能抛出 std::bad_allocT 的构造函数抛出的任何异常。如果抛出异常,则函数不起作用。 如果在数组构造期间抛出异常,则已初始化的元素将按相反顺序销毁。(自 C++20 起)

[编辑] 注意

这些函数通常会分配比 sizeof(T) 更多的内存,以便为内部簿记结构(如引用计数)留出空间。

这些函数可以用作 std::shared_ptr<T>(new T(args...)) 的替代方案。权衡之处在于

  • std::shared_ptr<T>(new T(args...)) 至少执行两次分配(一次用于对象 T,一次用于共享指针的控制块),而 std::make_shared<T> 通常只执行一次分配(标准建议这样做,但不要求;所有已知的实现都这样做)。
  • 如果在所有共享所有者的生命周期结束后,任何 std::weak_ptr 引用了 std::make_shared 创建的控制块,则 T 占用的内存将持续存在,直到所有弱所有者也被销毁,如果 sizeof(T) 很大,这可能是不可取的。
  • std::shared_ptr<T>(new T(args...)) 如果在可访问的上下文中执行,则可以调用 T 的非公共构造函数,而 std::make_shared 需要对所选构造函数的公共访问权限。
  • std::shared_ptr 构造函数不同,std::make_shared 不允许自定义析构器。
  • std::make_shared 使用 ::new,因此如果使用类特定的 operator new 设置了任何特殊行为,则它将与 std::shared_ptr<T>(new T(args...)) 不同。
(直到 C++20)
  • 诸如 f(std::shared_ptr<int>(new int(42)), g()) 之类的代码可能会导致内存泄漏,如果 gnew int(42) 之后被调用并抛出异常,而 f(std::make_shared<int>(42), g()) 是安全的,因为两个函数调用永远不会交错执行
(直到 C++17)

构造函数使用指针 ptr 启用 shared_from_this,类型为 U*,意味着它确定 U 是否具有 明确且可访问的(自 C++17 起) 基类,该基类是 std::enable_shared_from_this 的特化,如果是,则构造函数评估 if (ptr != nullptr && ptr->weak_this .expired())
    ptr->weak_this = std::shared_ptr<std::remove_cv_t<U>>
        (*this, const_cast<std::remove_cv_t<U>*>(ptr));

weak_this 的赋值不是原子的,并且与对同一对象的任何潜在并发访问冲突。这确保了将来对 shared_from_this() 的调用将与此原始指针构造函数创建的 std::shared_ptr 共享所有权。

上面的代码中的测试 ptr->weak_this .expired() 确保 weak_this 在已指示所有者的情况下不会被重新赋值。自 C++17 起,此测试是必需的。

特性测试 Std 特性
__cpp_lib_shared_ptr_arrays 201707L (C++20) std::make_shared 的数组支持;重载 (2-5)
__cpp_lib_smart_ptr_for_overwrite 202002L (C++20) 使用默认初始化创建智能指针 (std::allocate_shared_for_overwrite, std::make_shared_for_overwrite, std::make_unique_for_overwrite); 重载 (6,7)

[编辑] 示例

#include <iostream>
#include <memory>
#include <type_traits>
#include <vector>
 
struct C
{
    // constructors needed (until C++20)
    C(int i) : i(i) {}
    C(int i, float f) : i(i), f(f) {}
    int i;
    float f{};
};
 
int main()
{
    // using “auto” for the type of “sp1”
    auto sp1 = std::make_shared<C>(1); // overload (1)
    static_assert(std::is_same_v<decltype(sp1), std::shared_ptr<C>>);
    std::cout << "sp1->{ i:" << sp1->i << ", f:" << sp1->f << " }\n";
 
    // being explicit with the type of “sp2”
    std::shared_ptr<C> sp2 = std::make_shared<C>(2, 3.0f); // overload (1)
    static_assert(std::is_same_v<decltype(sp2), std::shared_ptr<C>>);
    static_assert(std::is_same_v<decltype(sp1), decltype(sp2)>);
    std::cout << "sp2->{ i:" << sp2->i << ", f:" << sp2->f << " }\n";
 
    // shared_ptr to a value-initialized float[64]; overload (2):
    std::shared_ptr<float[]> sp3 = std::make_shared<float[]>(64);
 
    // shared_ptr to a value-initialized long[5][3][4]; overload (2):
    std::shared_ptr<long[][3][4]> sp4 = std::make_shared<long[][3][4]>(5);
 
    // shared_ptr to a value-initialized short[128]; overload (3):
    std::shared_ptr<short[128]> sp5 = std::make_shared<short[128]>();
 
    // shared_ptr to a value-initialized int[7][6][5]; overload (3):
    std::shared_ptr<int[7][6][5]> sp6 = std::make_shared<int[7][6][5]>();
 
    // shared_ptr to a double[256], where each element is 2.0; overload (4):
    std::shared_ptr<double[]> sp7 = std::make_shared<double[]>(256, 2.0);
 
    // shared_ptr to a double[7][2], where each double[2]
    // element is {3.0, 4.0}; overload (4):
    std::shared_ptr<double[][2]> sp8 = std::make_shared<double[][2]>(7, {3.0, 4.0});
 
    // shared_ptr to a vector<int>[4], where each vector
    // has contents {5, 6}; overload (4):
    std::shared_ptr<std::vector<int>[]> sp9 =
        std::make_shared<std::vector<int>[]>(4, {5, 6});
 
    // shared_ptr to a float[512], where each element is 1.0; overload (5):
    std::shared_ptr<float[512]> spA = std::make_shared<float[512]>(1.0);
 
    // shared_ptr to a double[6][2], where each double[2] element
    // is {1.0, 2.0}; overload (5):
    std::shared_ptr<double[6][2]> spB = std::make_shared<double[6][2]>({1.0, 2.0});
 
    // shared_ptr to a vector<int>[4], where each vector
    // has contents {5, 6}; overload (5):
    std::shared_ptr<std::vector<int>[4]> spC =
        std::make_shared<std::vector<int>[4]>({5, 6});
}

输出

sp1->{ i:1, f:0 }
sp2->{ i:2, f:3 }

[编辑] 缺陷报告

以下行为变更缺陷报告已追溯应用于先前发布的 C++ 标准。

DR 应用于 已发布行为 正确行为
LWG 4024 C++20 不清楚在
std::make_shared_for_overwrite 中构造的对象是如何销毁的
已明确

[编辑] 参见

构造新的 shared_ptr
(公共成员函数) [编辑]
创建管理使用分配器分配的新对象的共享指针
(函数模板) [编辑]
允许对象创建指向自身的 shared_ptr
(类模板) [编辑]
创建管理新对象的唯一指针
(函数模板) [编辑]
分配函数
(函数) [编辑]