std::make_shared, std::make_shared_for_overwrite

来自cppreference.com
< cpp‎ | memory‎ | shared ptr
 
 
内存管理库
(仅用于阐述*)
分配器
未初始化内存算法
受约束的未初始化内存算法
内存资源
未初始化存储 (C++20 前)
(C++17 弃用)
(C++17 弃用)

垃圾收集器支持 (C++23 前)
(C++11)(C++23 前)
(C++11)(C++23 前)
(C++11)(C++23 前)
(C++11)(C++23 前)
(C++11)(C++23 前)
(C++11)(C++23 前)
 
 
在标头 <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 是指向适合持有 T 类型对象的存储的 void* 指针。如果该对象要被销毁,那么它会如同以 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 是指向适合持有 T 类型对象的存储的 void* 指针。如果该对象要被销毁,那么它会如同以 pt->~T() 被销毁,其中 pt 是指向该 T 类型对象的指针。
  • 如果 T 是有边界的数组类型,那么不指定每个元素的初始值。
此重载只有在 T 不是数组类型,或者是有边界的数组类型时才会参与重载决议。
7) 该对象具有 std::remove_extent_t<T>[N] 类型。不指定每个元素的初始值。
此重载只有在 T 是无边界的数组类型时才会参与重载决议。

目录

初始化和销毁数组元素

U 类型的数组元素会以它们地址的升序进行初始化。

  • 如果 U 不是数组类��,那么每个元素如同以以下表达式构造,其中 pv 是指向适合持有 U 类型对象的存储的 void* 指针:
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 类型或在 T 是无边界的数组类型时具有 std::remove_extent_t<T>[N] 类型(C++20 起)的对象的 std::shared_ptr

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

[编辑] 异常

可能抛出 std::bad_alloc 或任何 T 构造函数所抛的异常。如果抛出异常,那么函数无效果。如果异常在数组的构造中抛出,那么已初始化元素以逆序销毁。(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) 较大时这可能是不想要的行为。
  • 如果在可访问 T 的非公开构造函数的语境中执行,那么 std::shared_ptr<T>(new T(args...)) 可能会调用它,而 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 前)

构造函数以 U* 类型指针 ptr 启用 shared_from_this,表示它确定 U 是否拥有作为 std::enable_shared_from_this 特化的无歧义且可访问(C++17 起)基类,在是的情况下会求值 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 起要求此测试。

功能特性测试 标准 功能特性
__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_overwritestd::make_shared_for_overwritestd::make_unique_for_overwrite);重载 (6,7)

[编辑] 示例

#include <iostream>
#include <memory>
#include <type_traits>
#include <vector>
 
struct C
{
    // < 需要构造函数 (C++20 前)
    C(int i) : i(i) {}
    C(int i, float f) : i(i), f(f) {}
    int i;
    float f{};
};
 
int main()
{
    // 为 “sp1” 的类型使用 “auto”
    auto sp1 = std::make_shared<C>(1); // 重载 (1)
    static_assert(std::is_same_v<decltype(sp1), std::shared_ptr<C>>);
    std::cout << "sp1->{ i:" << sp1->i << ", f:" << sp1->f << " }\n";
 
    // 明确 “sp2” 的类型
    std::shared_ptr<C> sp2 = std::make_shared<C>(2, 3.0f); // 重载 (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";
 
    // 指向值初始化的 float[64] 的 shared_ptr;重载 (2):
    std::shared_ptr<float[]> sp3 = std::make_shared<float[]>(64);
 
    // 指向值初始化的 long[5][3][4] 的 shared_ptr;重载 (2):
    std::shared_ptr<long[][3][4]> sp4 = std::make_shared<long[][3][4]>(5);
 
    // 指向值初始化的 short[128] 的 shared_ptr;重载 (3):
    std::shared_ptr<short[128]> sp5 = std::make_shared<short[128]>();
 
    // 指向值初始化的 int[7][6][5] 的 shared_ptr;重载 (3):
    std::shared_ptr<int[7][6][5]> sp6 = std::make_shared<int[7][6][5]>();
 
    // 指向 double[256] 的 shared_ptr,其各元素均为 2.0;重载 (4):
    std::shared_ptr<double[]> sp7 = std::make_shared<double[]>(256, 2.0);
 
    // 指向 double[7][2] 的 shared_ptr,其各 double[2] 元素均为 {3.0, 4.0};重载 (4):
    std::shared_ptr<double[][2]> sp8 = std::make_shared<double[][2]>(7, {3.0, 4.0});
 
    // 指向 vector<int>[4] 的 shared_ptr,其各向量的内容均为 {5, 6};重载 (4):
    std::shared_ptr<std::vector<int>[]> sp9 =
        std::make_shared<std::vector<int>[]>(4, {5, 6});
 
    // 指向 float[512] 的 shared_ptr,其各元素均为 1.0;重载 (5):
    std::shared_ptr<float[512]> spA = std::make_shared<float[512]>(1.0);
 
    // 指向 double[6][2] 的 shared_ptr,其各 double[2] 元素均为 {1.0, 2.0};重载 (5):
    std::shared_ptr<double[6][2]> spB = std::make_shared<double[6][2]>({1.0, 2.0});
 
    // 指向 vector<int>[4] 的 shared_ptr,其各向量的内容均为 {5, 6};重载 (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++ 标准。

缺陷报告 应用于 出版时的行为 正确行为
LWG 4024 C++20 不明确如何销毁 std::make_shared_for_overwrite 中构造的对象 使之明确

[编辑] 参阅

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