std::shared_ptr<T>::shared_ptr
声明
// 1)
constexpr shared_ptr() noexcept;
// 2)
constexpr shared_ptr( std::nullptr_t ) noexcept;
// 3)
template< class Y >
explicit shared_ptr( Y* ptr );
// 4)
template< class Y, class Deleter >
shared_ptr( Y* ptr, Deleter d );
// 5)
template< class Deleter >
shared_ptr( std::nullptr_t ptr, Deleter d );
// 6)
template< class Y, class Deleter, class Alloc >
shared_ptr( Y* ptr, Deleter d, Alloc alloc );
// 7)
template< class Deleter, class Alloc >
shared_ptr( std::nullptr_t ptr, Deleter d, Alloc alloc );
// 8)
template< class Y >
shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept;
// 9)
template< class Y >
shared_ptr( shared_ptr<Y>&& r, element_type* ptr ) noexcept; // Since C++20
// 10)
shared_ptr( const shared_ptr& r ) noexcept;
// 11)
template< class Y >
shared_ptr( const shared_ptr<Y>& r ) noexcept;
// 12)
shared_ptr( shared_ptr&& r ) noexcept;
// 13)
template< class Y >
shared_ptr( shared_ptr<Y>&& r ) noexcept;
// 14)
template< class Y >
explicit shared_ptr( const std::weak_ptr<Y>& r );
// 15)
template< class Y >
shared_ptr( std::auto_ptr<Y>&& r ); // Removed in C++17
// 16)
template< class Y, class Deleter >
shared_ptr( std::unique_ptr<Y, Deleter>&& r );
从各种指向要管理的对象指针类型构造新的 shared_ptr
。
为了以下描述的目的,当指针类型 Y*
可转换为指针类型 T*
,或者 Y
是数组类型 U[N]
且 T
是 U cv []
(其中 cv
是一组 cv-限定符)时,称指针类型 Y*
与指针类型 T*
兼容。
1-2)
构造一个不管理对象的 shared_ptr
,即空的 shared_ptr
3-7)
构造一个以 ptr
作为被管理对象指针的 shared_ptr
-
对于(3-4,6),
Y*
必须可转换为T*
(直到 C++17)。 -
如果 T 是数组类型
U[N]
,则如果Y(*)[N]
不能转换为T*
,(3-4,6)不参与重载解析。如果T
是数组类型U[]
,则如果Y(*)[]
不能转换为T*
,(3-4,6)不参与重载解析。否则,如果Y*
不能转换为T*
,(3-4,6)不参与重载解析(自 C++17 起)。
此外
-
3) 当
T
不是数组类型时,使用delete ptr
;当T
是数组类型时,使用delete[] ptr
(自 C++17 起)作为析构器。Y
必须是完整类型。delete 表达式必须是格式正确的,具有明确定义的行为,并且不抛出任何异常。此构造函数此外不参与重载解析,如果 delete 表达式不是格式正确的(自 C++17 起)。 -
4-5) 使用指定的析构器
d
作为析构器。表达式d(ptr)
必须是格式正确的,具有明确定义的行为,并且不抛出任何异常。d
的构造以及从其复制的存储的析构器不得抛出异常。-
析构器必须是
CopyConstructible
(直到 C++17)。 -
这些构造函数此外不参与重载解析,如果表达式
d(ptr)
不是格式正确的,或者std::is_move_constructible<D>::value
为false
(自 C++17 起)。
-
-
6-7) 与 (4-5) 相同,但额外使用
alloc
的副本进行内部数据分配。Alloc
必须是Allocator
。
-
别名构造函数:构造一个 shared_ptr
,该 shared_ptr
与 r
的初始值共享所有权信息,但持有一个不相关且未被管理的指针 ptr
。如果此 shared_ptr
是该组中最后一个离开作用域的,它将调用为 r
最初管理的对象存储的析构器。但是,对此 shared_ptr
调用 get()
将始终返回 ptr
的副本。程序员有责任确保此 ptr
在此 shared_ptr
存在期间保持有效,例如在典型的用例中,ptr
是 r
管理的对象的一个成员,或者 r.get()
的别名(例如,向下转型)。对于接受右值引用的第二个重载,调用后 r
为空且 r.get() == nullptr
(自 C++20 起)。
构造一个 shared_ptr
,它共享 r
管理的对象的拥有权。如果 r
不管理任何对象,则 *this
也不管理任何对象。如果 Y*
不能隐式 *转换为(直到 C++17)/ 兼容于(自 C++17 起) T*
,则模板重载不参与重载解析。
从 r
移动构造一个 shared_ptr
。构造后,*this
包含 r
先前状态的副本,r
为空且其存储的指针为 null
。如果 Y*
不能隐式 *转换为(直到 C++17)/ 兼容于(自 C++17 起) T*
,则模板重载不参与重载解析。
构造一个 shared_ptr
,它共享 r
管理的对象的拥有权。Y*
必须隐式转换为 T*
(直到 C++17)。此重载仅在 Y*
与 T*
兼容时参与重载解析(自 C++17 起)。请注意,r.lock()
可用于相同目的:区别在于此构造函数在参数为空时抛出异常,而 std::weak_ptr<T>::lock()
在此情况下构造一个空的 std::shared_ptr
。
构造一个 shared_ptr
,该 shared_ptr
存储并拥有 r
原先拥有的对象。Y*
必须可转换为 T*
。构造后,r
为空。
构造一个 shared_ptr
,它管理 r
当前管理的对象。与 r
关联的析构器被存储用于将来被管理对象的删除。调用后 r
不管理任何对象。
- 如果
std::unique_ptr<Y, Deleter>::pointer
与T*
不兼容,此重载不参与重载解析。如果r.get()
是null
指针,此重载等同于默认构造函数 (1)(自 C++17 起)。
如果 Deleter
是引用类型,则等同于 shared_ptr(r.release()
, std::ref(r.get_deleter())
。否则,等同于 shared_ptr(r.release(), std::move(r.get_deleter()))
当 T
不是数组类型时,重载 (3)、(4) 和 (6) 使用指针 ptr
启用 shared_from_this
,而重载 (13) 使用 r.release()
返回的指针启用 shared_from_this
。
参数
ptr
- 指向要管理的对象指针 d
- 用于销毁对象的析构器 alloc
- 用于内部数据分配的分配器 r
- 另一个智能指针,用于共享拥有权或从其获取拥有权
异常
如果无法获得所需的额外内存,则抛出 std::bad_alloc
。可能因其他错误而抛出实现定义的异常。如果发生异常,则调用 delete ptr
-如果 T 不是数组类型,否则调用 delete[] ptr
(自 C++17 起).
4-7)
如果无法获得所需的额外内存,则抛出 std::bad_alloc
。可能因其他错误而抛出实现定义的异常。如果发生异常,则调用 d(ptr)
。
11)
如果 r.expired() == true
,则抛出 std::bad_weak_ptr
。在此情况下,构造函数无效。
12)
如果无法获得所需的额外内存,则抛出 std::bad_alloc
。可能因其他错误而抛出实现定义的异常。如果发生异常,此构造函数无效。
13)
如果抛出异常,则构造函数无效。
备注
当构造函数使用类型为 U*
的指针 ptr
启用 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_this
是 std::enable_shared_from_this
的隐藏可变 std::weak_ptr
成员。对 weak_this
成员的赋值不是原子的,并且与对同一对象的任何潜在并发访问冲突。这确保了未来对 shared_from_this()
的调用将与此原始指针构造函数创建的 std::shared_ptr
共享拥有权。
测试 ptr->weak_this.expired()
在上述说明代码中确保如果 weak_this
已经指示了所有者,则不会重新赋值。*此测试是 C++17 所必需的*。
原始指针重载假定拥有所指向的对象。因此,使用原始指针重载构造一个对象(例如,通过 shared_ptr(ptr.get())
)已经被 shared_ptr
管理的对象,很可能导致未定义行为,即使该对象是 std::enable_shared_from_this
的派生类型。
由于默认构造函数是 *constexpr*,*静态* shared_ptrs
作为静态非局部初始化的一部分进行初始化,早于任何动态非局部初始化开始。这使得在任何静态对象的构造函数中使用 shared_ptr
是安全的。
在 C++11 和 C++14 中,可以从 std::unique_ptr<T[]>
构造 std::shared_ptr<T>
std::unique_ptr<int[]> arr(new int[1]);
std::shared_ptr<int> ptr(std::move(arr));
由于 shared_ptr
从 std::unique_ptr
获取其析构器(一个 std::default_delete<T[]>
对象),数组将被正确释放。
这在 C++17 中不再允许。现在应该使用数组形式 std::shared_ptr<T[]>
。
示例
#include <memory>
#include <iostream>
struct Foo {
int id{0};
Foo(int i = 0) : id{i} { std::cout << "Foo::Foo(" << i << ")\n"; }
~Foo() { std::cout << "Foo::~Foo(), id=" << id << '\n'; }
};
struct D {
void operator()(Foo* p) const {
std::cout << "Call delete from function object. Foo::id=" << p->id << '\n';
delete p;
}
};
int main()
{
{
std::cout
<< "1) constructor with no managed object\n";
std::shared_ptr<Foo> sh1;
}
{
std::cout
<< "2) constructor with object\n";
std::shared_ptr<Foo> sh2(new Foo{10});
std::cout
<< "sh2.use_count(): "
<< sh2.use_count() << '\n';
std::shared_ptr<Foo> sh3(sh2);
std::cout
<< "sh2.use_count(): "
<< sh2.use_count() << '\n';
std::cout
<< "sh3.use_count(): "
<< sh3.use_count() << '\n';
}
{
std::cout << "3) constructor with object and deleter\n";
std::shared_ptr<Foo> sh4(new Foo{11}, D());
std::shared_ptr<Foo> sh5(new Foo{12}, [](auto p) {
std::cout
<< "Call delete from lambda... p->id="
<< p->id << "\n";
delete p;
});
}
}
1) constructor with no managed object
2) constructor with object
Foo::Foo(10)
sh2.use_count(): 1
sh2.use_count(): 2
sh3.use_count(): 2
Foo::~Foo(), id=10
3) constructor with object and deleter
Foo::Foo(11)
Foo::Foo(12)
Call delete from lambda... p->id=12
Foo::~Foo(), id=12
Call delete from function object. Foo::id=11
Foo::~Foo(), id=11
缺陷报告
以下改变行为的缺陷报告已追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 发布时的行为 | 正确行为 |
---|---|---|---|
LWG 3548 | C++11 | 从 unique_ptr 复制构造的析构器 | 改为移动构造 |