跳到主要内容

std::make_shared, std::make_shared_for_overwrite

定义于头文件 <memory>

声明

自 C++11 起
// 1)
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args ); // T is not array

C++20

// 2)
template< class T >
shared_ptr<T> make_shared( std::size_t N ); // T is U[]
// 3)
template< class T >
shared_ptr<T> make_shared(); // T is U[N]
// 4)
template< class T >
shared_ptr<T> make_shared( std::size_t N, const std::remove_extent_t<T>& u ); // T is U[]
// 5)
template< class T >
shared_ptr<T> make_shared( const std::remove_extent_t<T>& u ); // T is U[N]
// 6)
template< class T >
shared_ptr<T> make_shared_for_overwrite(); // T is not U[]
// 7)
template< class T >
shared_ptr<T> make_shared_for_overwrite( std::size_t N ); // T is U[]

使用 args 作为 T 的构造函数的参数列表,构造一个类型为 T 的对象并将其包装在 std::shared_ptr 中。该对象通过表达式 ::new (pv) T(std::forward<Args>(args)...) 来构造,其中 pv 是一个内部的 void* 指针,指向适合容纳类型为 T 的对象的存储。该存储通常比 sizeof(T) 大,以便将共享指针的控制块和 T 对象放在一次分配中。此函数调用的 std::shared_ptr 构造函数可以使用新构造的类型为 T 的对象的指针来启用 shared_from_this。此重载仅在 T 不是数组类型时参与重载解析。
2,3)
与 (1) 相同,但构造的对象是一个可能多维的数组,其类型为 std::remove_all_extents_t<T> 的非数组元素通过放置 new 表达式 ::new(pv) std::remove_all_extents_t<T>() 进行值初始化。重载 (2) 在第一维上创建一个大小为 N 的数组。数组元素按地址升序初始化,当它们的生命周期结束时,按照它们原始构造的相反顺序销毁。
4,5)
与 (2,3) 相同,但每个元素都从默认值 u 进行初始化。如果 U 不是数组类型,则执行方式与 (1) 中的相同放置 new 表达式相同;否则,则通过将 u 中的相应元素与 (1) 中的相同放置 new 表达式初始化(可能多维的)数组的每个非数组元素来执行。重载 (4) 在第一维上创建一个大小为 N 的数组。数组元素按地址升序初始化,当它们的生命周期结束时,按照它们原始构造的相反顺序销毁。
6)
如果 T 不是数组类型,则与 (1) 相同;如果 TU[N],则与 (3) 相同,但创建的对象是默认初始化的。
7)
与 (2) 相同,但各个数组元素是默认初始化的。

在每种情况下,对象(如果 T 是数组类型,则为各个元素)都将通过 p->~X() 进行销毁,其中 p 是指向对象的指针,X 是其类型。

参数

args - 用于构造 T 实例的参数列表。N - 用于数组的大小 u - 用于初始化数组每个元素的初始值。

返回值

类型为 T 的实例的 std::shared_ptr

异常

可能抛出 std::bad_allocT 的构造函数抛出的任何异常。如果抛出异常,则函数无效。如果在数组构造期间抛出异常,则已初始化的元素将按相反顺序销毁。

备注

此函数可作为 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

  • std::shared_ptr 支持数组类型(从 C++17 开始),但 std::make_shared 不支持。此功能由 boost::make_shared 支持。

直到 C++17

  • f(std::shared_ptr<int>(new int(42)), g()) 这样的代码可能导致内存泄漏,如果 gnew int(42) 之后被调用并抛出异常,而 f(std::make_shared<int>(42), g()) 是安全的,因为两个函数调用永远不会交错。

构造函数通过指针 ptr(类型为 U*)启用 shared_from_this,这意味着它会确定 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_thisstd::enable_shared_from_this 的隐藏可变 std::weak_ptr 成员。对 weak_this 成员的赋值不是原子的,并且会与对同一对象的任何潜在并发访问发生冲突。这确保了将来对 shared_from_this() 的调用将与此原始指针构造函数创建的 std::shared_ptr 共享所有权。

上述示例代码中的测试 ptr->weak_this.expired() 确保如果 weak_this 已经指示了一个所有者,它不会被重新赋值。此测试自 C++17 起是必需的。

特性测试宏标准注释
__cpp_lib_shared_ptr_arrays201707L(C++20)std::make_shared 的数组支持,用于重载 (2-5)。
__cpp_lib_smart_ptr_for_overwrite202002L(C++20)智能指针创建带默认初始化,用于重载 (6,7)。

示例

#include <memory>
#include <vector>
#include <iostream>
#include <type_traits>

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 }

std::make_shared, std::make_shared_for_overwrite

定义于头文件 <memory>

声明

自 C++11 起
// 1)
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args ); // T is not array

C++20

// 2)
template< class T >
shared_ptr<T> make_shared( std::size_t N ); // T is U[]
// 3)
template< class T >
shared_ptr<T> make_shared(); // T is U[N]
// 4)
template< class T >
shared_ptr<T> make_shared( std::size_t N, const std::remove_extent_t<T>& u ); // T is U[]
// 5)
template< class T >
shared_ptr<T> make_shared( const std::remove_extent_t<T>& u ); // T is U[N]
// 6)
template< class T >
shared_ptr<T> make_shared_for_overwrite(); // T is not U[]
// 7)
template< class T >
shared_ptr<T> make_shared_for_overwrite( std::size_t N ); // T is U[]

使用 args 作为 T 的构造函数的参数列表,构造一个类型为 T 的对象并将其包装在 std::shared_ptr 中。该对象通过表达式 ::new (pv) T(std::forward<Args>(args)...) 来构造,其中 pv 是一个内部的 void* 指针,指向适合容纳类型为 T 的对象的存储。该存储通常比 sizeof(T) 大,以便将共享指针的控制块和 T 对象放在一次分配中。此函数调用的 std::shared_ptr 构造函数可以使用新构造的类型为 T 的对象的指针来启用 shared_from_this。此重载仅在 T 不是数组类型时参与重载解析。
2,3)
与 (1) 相同,但构造的对象是一个可能多维的数组,其类型为 std::remove_all_extents_t<T> 的非数组元素通过放置 new 表达式 ::new(pv) std::remove_all_extents_t<T>() 进行值初始化。重载 (2) 在第一维上创建一个大小为 N 的数组。数组元素按地址升序初始化,当它们的生命周期结束时,按照它们原始构造的相反顺序销毁。
4,5)
与 (2,3) 相同,但每个元素都从默认值 u 进行初始化。如果 U 不是数组类型,则执行方式与 (1) 中的相同放置 new 表达式相同;否则,则通过将 u 中的相应元素与 (1) 中的相同放置 new 表达式初始化(可能多维的)数组的每个非数组元素来执行。重载 (4) 在第一维上创建一个大小为 N 的数组。数组元素按地址升序初始化,当它们的生命周期结束时,按照它们原始构造的相反顺序销毁。
6)
如果 T 不是数组类型,则与 (1) 相同;如果 TU[N],则与 (3) 相同,但创建的对象是默认初始化的。
7)
与 (2) 相同,但各个数组元素是默认初始化的。

在每种情况下,对象(如果 T 是数组类型,则为各个元素)都将通过 p->~X() 进行销毁,其中 p 是指向对象的指针,X 是其类型。

参数

args - 用于构造 T 实例的参数列表。N - 用于数组的大小 u - 用于初始化数组每个元素的初始值。

返回值

类型为 T 的实例的 std::shared_ptr

异常

可能抛出 std::bad_allocT 的构造函数抛出的任何异常。如果抛出异常,则函数无效。如果在数组构造期间抛出异常,则已初始化的元素将按相反顺序销毁。

备注

此函数可作为 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

  • std::shared_ptr 支持数组类型(从 C++17 开始),但 std::make_shared 不支持。此功能由 boost::make_shared 支持。

直到 C++17

  • f(std::shared_ptr<int>(new int(42)), g()) 这样的代码可能导致内存泄漏,如果 gnew int(42) 之后被调用并抛出异常,而 f(std::make_shared<int>(42), g()) 是安全的,因为两个函数调用永远不会交错。

构造函数通过指针 ptr(类型为 U*)启用 shared_from_this,这意味着它会确定 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_thisstd::enable_shared_from_this 的隐藏可变 std::weak_ptr 成员。对 weak_this 成员的赋值不是原子的,并且会与对同一对象的任何潜在并发访问发生冲突。这确保了将来对 shared_from_this() 的调用将与此原始指针构造函数创建的 std::shared_ptr 共享所有权。

上述示例代码中的测试 ptr->weak_this.expired() 确保如果 weak_this 已经指示了一个所有者,它不会被重新赋值。此测试自 C++17 起是必需的。

特性测试宏标准注释
__cpp_lib_shared_ptr_arrays201707L(C++20)std::make_shared 的数组支持,用于重载 (2-5)。
__cpp_lib_smart_ptr_for_overwrite202002L(C++20)智能指针创建带默认初始化,用于重载 (6,7)。

示例

#include <memory>
#include <vector>
#include <iostream>
#include <type_traits>

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 }