定义于头文件
|
||
template< class T, class... Args > shared_ptr<T> make_shared( Args&&... args ); |
(1) | (C++11 起) (T 非数组) |
template<class T> shared_ptr<T> make_shared(std::size_t N); |
(2) | (C++20 起) (T 为 U[]) |
template<class T> shared_ptr<T> make_shared(); |
(3) | (C++20 起) (T 为 U[N]) |
template<class T> shared_ptr<T> make_shared(std::size_t N, const std::remove_extent_t<T>& u); |
(4) | (C++20 起) (T 为 U[]) |
template<class T> shared_ptr<T> make_shared(const std::remove_extent_t<T>& u); |
(5) | (C++20 起) (T 为 U[N]) |
args
为
T
的构造函数参数列表,构造
T
类型对象并将它包装于
std::shared_ptr
。对象如同用表达式
::new (pv) T(std::forward<Args>(args)...)
构造,其中
pv
是内部指向适合保有
T
类型对象的存储的
void*
指针。存储典型地大于
sizeof(T)
,以对共享指针控制块和
T
对象使用一次分配。此函数所调用的
std::shared_ptr
构造函数以指向新构造的
T
类型对象指针启用
shared_from_this
。此重载仅若 T 不是数组类型才参与重载决议。
std::remove_all_extents_t
类型的非数组元素如同以布置 new 表达式
::new(pv) std::remove_all_extents_t<T>() 值初始化。重载
(2) 创建第一维上大小为
N
的数组。数组元素以其地址递增顺序初始化,而当其生存期结束时,以原本构造顺序的逆序销毁。
u
初始化。若
U
不是数组类型,则这如同以如
(1) 中的布置 new 表达式进行;否则,这如同以如同
(1) 中的布置 new 表达式,从来自
u
的对应元素初始化(可能多维的)数组的每个非数组元素。重载
(4) 创建第一维上大小为
N
的数组。数组元素以其地址递增顺序初始化,而当其生存期结束时,以原本构造顺序的逆序销毁。
args | - | 将用以构造 T 实例的参数列表。 |
N | - | 要使用的数组 |
u | - | 初始化数组每个元素的初值 |
类型 T
实例的 std::shared_ptr 。
可能抛出 std::bad_alloc 或任何 T
构造函数所抛的异常。若抛出异常,则函数无效果。若异常在数组的构造中抛出,则已初始化元素以逆序销毁。 (C++20 起)
此函数可用作 std::shared_ptr<T>(new T(args...)) 的替代品。得失是:
T
而另一次为共享指针的控制块),而 std::make_shared<T> 典型地仅进行一次分配(标准推荐但不要求如此,所有已知实现均如此)。std::make_shared
所创建的控制块,则 T
所占有的内存维持着,直至所有弱拥有者亦被销毁,若 sizeof(T)
较大则这可能是不想要的。T
的非公开构造函数,若在它可访问的语境中执行,而 std::make_shared
要求对被选择构造函数的公开访问。std::make_shared
不允许自定义删除器。std::make_shared
使用 ::new ,故若用类限定的 operator new 设置了任何特殊行为,则它将异于 std::shared_ptr<T>(new T(args...)) 。
|
(C++20 前) |
|
(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
是 std::shared_from_this 的隐藏 mutable std::weak_ptr
成员。对 weak_this
成员的赋值不是原子的,且与任何到同一对象的潜在并发访问冲突。这确保将来对 shared_from_this() 调用,将与此裸指针构造函数所创建的 shared_ptr
共享所有权。
上述解释代码中,测试 ptr->weak_this.expired()
是为确保若 weak_this
指示已有占有者则重赋值它。从 C++17 起要求此测试。
#include#include void foo(const std::shared_ptr<int>& i) { (*i)++; } int main() { auto sp = std::make_shared<int>(12); foo(sp); std::cout << *sp << '\n'; }
输出:
13
(构造函数)
|
构造新的 shared_ptr (公开成员函数) |
allocate_shared
|
创建共享指针,管理用分配器分配的新对象 (函数模板) |
make_unique
(C++14)
|
创建管理新对象的独有指针 (函数模板) |
operator new
operator new[] |
分配函数 (函数) |
C++11 中引入了智能指针, 同时还有一个模板函数 std::make_shared
可以返回一个指定类型的 std::shared_ptr
, 那与 std::shared_ptr
的构造函数相比它能给我们带来什么好处呢 ?
shared_ptr
需要维护引用计数的信息,
如果你通过使用原始的 new 表达式分配对象, 然后传递给 shared_ptr (也就是使用 shared_ptr 的构造函数) 的话, shared_ptr 的实现没有办法选择, 而只能单独的分配控制块:
1
2
|
|
如果选择使用 make_shared
的话, 情况就会变成下面这样:
1
|
|
内存分配的动作, 可以一次性完成. 这减少了内存分配的次数, 而内存分配是代价很高的操作.
关于两种方式的性能测试可以看这里 Experimenting with C++ std::make_shared
看看下面的代码:
1
2
3
4
|
|
C++ 是不保证参数求值顺序, 以及内部表达式的求值顺序的, 所以可能的执行顺序如下:
好了, 现在我们假设在第 2 步的时候, 抛出了一个异常 (比如 out of memory, 总之, Rhs 的构造函数异常了), 那么第一步申请的 Lhs 对象内存泄露了. 这个问题的核心在于, shared_ptr 没有立即获得裸指针.
我们可以用如下方式来修复这个问题.
1
2
3
|
|
当然, 推荐的做法是使用 std::make_shared
来代替:
1
|
|
make_shared
虽好, 但也存在一些问题, 比如, 当我想要创建的对象没有公有的构造函数时, make_shared
就无法使用了, 当然我们可以使用一些小技巧来解决这个问题, 比如这里 How do I call ::std::make_shared on a class with only protected or private constructors?
make_shared
只分配一次内存, 这看起来很好. 减少了内存分配的开销. 问题来了, weak_ptr
会保持控制块(强引用, 以及弱引用的信息)的生命周期, 而因此连带着保持了对象分配的内存, 只有最后一个 weak_ptr
离开作用域时, 内存才会被释放. 原本强引用减为 0 时就可以释放的内存, 现在变为了强引用, 若引用都减为 0 时才能释放, 意外的延迟了内存释放的时间. 这对于内存要求高的场景来说, 是一个需要注意的问题. 关于这个问题可以看这里 make_shared, almost a silver bullet