std::shared_ptr<T>::shared_ptr

来自cppreference.com
 
 
内存管理库
(仅用于阐述*)
分配器
未初始化内存算法
受约束的未初始化内存算法
内存资源
未初始化存储 (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 前)
 
 
constexpr shared_ptr() noexcept;
(1)
constexpr shared_ptr( std::nullptr_t ) noexcept;
(2)
template< class Y > 
explicit shared_ptr( Y* ptr );
(3) (C++26 起为 constexpr)
template< class Y, class Deleter > 
shared_ptr( Y* ptr, Deleter d );
(4) (C++26 起为 constexpr)
template< class Deleter > 
shared_ptr( std::nullptr_t ptr, Deleter d );
(5) (C++26 起为 constexpr)
template< class Y, class Deleter, class Alloc > 
shared_ptr( Y* ptr, Deleter d, Alloc alloc );
(6) (C++26 起为 constexpr)
template< class Deleter, class Alloc > 
shared_ptr( std::nullptr_t ptr, Deleter d, Alloc alloc );
(7) (C++26 起为 constexpr)
template< class Y > 
shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept;
(8) (C++26 起为 constexpr)
template< class Y > 
shared_ptr( shared_ptr<Y>&& r, element_type* ptr ) noexcept;
(9) (C++20 起)
(C++26 起为 constexpr)
shared_ptr( const shared_ptr& r ) noexcept;
(10) (C++26 起为 constexpr)
template< class Y > 
shared_ptr( const shared_ptr<Y>& r ) noexcept;
(11) (C++26 起为 constexpr)
shared_ptr( shared_ptr&& r ) noexcept;
(12) (C++26 起为 constexpr)
template< class Y > 
shared_ptr( shared_ptr<Y>&& r ) noexcept;
(13) (C++26 起为 constexpr)
template< class Y > 
explicit shared_ptr( const std::weak_ptr<Y>& r );
(14) (C++26 起为 constexpr)
template< class Y > 
shared_ptr( std::auto_ptr<Y>&& r );
(15) (C++17 移除)
template< class Y, class Deleter > 
shared_ptr( std::unique_ptr<Y, Deleter>&& r );
(16) (C++26 起为 constexpr)

从指代要管理的对象的各种指针类型构造新的 shared_ptr

为以下描述的目的,如果指针类型 Y* 可转换到指针类型 T*,或 YU[N] 类型数组而 TU cv [](其中 cv 是某个 cv 限定符集合),那么称 Y* 兼容 T*

(C++17 起)
1,2) 构造无被管理对象的 shared_ptr,即空 shared_ptr
3-7) 构造 shared_ptr,管理 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 不是数组类型,那么(C++17 起)delete 表达式 delete ptr 为删除器;如果 T 是数组类型,那么以 delete[] ptr 为删除器(C++17 起)Y 必须是完整类型。delete 表达式必须良构,拥有良好定义行为且不抛异常。如果 delete 表达式非良构,那么此构造函数不参与重载决议。(C++17 起)
4,5) 以指定的删除器 d 为删除器。表达式 d(ptr) 必须良构,拥有良好定义行为且不抛异常。d 的构造和从它复制并存储的删除器的构造必须不抛异常。

Deleter 必须可复制构造 (CopyConstructible)

(C++17 前)

如果表达式 d(ptr) 非良构或 std::is_move_constructible_v<D>false,那么这些构造函数也不参与重载决议。

(C++17 起)
6,7)(4,5),但额外地用 alloc 的副本分配内部使用的数据。Alloc 必须是分配器 (Allocator)
8,9) 别名使用构造函数:构造 shared_ptr,与 r 的初始值共享所有权信息,但保有无关且不管理的指针 ptr。如果此 shared_ptr 是离开作用域的组中的最后者,那么它将调用最初 r 所管理对象的析构函数。然而,在此 shared_ptr 上调用 get() 将始终返回 ptr 的副本。程序员负责确保只要此 shared_ptr 存在,此 ptr 就保持合法,例如在典型使用情况中,其中 ptrr 所管理对象的成员,或是 r.get() 的别名(例如向下转型)。
9) 调用后 r 为空且 r.get() == nullptr
10,11) 构造 shared_ptr,共享 r 所管理对象的所有权。如果 r 不管理对象,那么 *this 也不管理对象。如果 Y*可隐式转换到(C++17 前)兼容(C++17 起) T*,那么此模板重载不参与重载决议。
12,13)r 移动构造 shared_ptr。构造后,*thisr 先前状态的副本,而 r 为空且它存储的指针为空。如果 Y*可隐式转换到(C++17 前)兼容(C++17 起) T*,那么此模板重载不参与重载决议。
14) 构造 shared_ptr,共享 r 所管理对象的所有权。Y* 必须可隐式转换到 T*(C++17 前)此重载只有在 Y* 兼容 T* 时才会参与重载决议。(C++17 起)注意可为相同目的用 r.lock():区别是在实参为空时此构造函数抛异常,而 std::weak_ptr<T>::lock() 在该情况下构造空的 std::shared_ptr
15) 构造 shared_ptr,存储并拥有 r 先前拥有的对象。Y* 必须可转换到 T*。构造后,r 为空。
16) 构造 shared_ptr,管理当前 r 所管理的对象。存储与 r 关联的删除器以在未来删除被管理对象。调用后 r 不管理对象。
如果 std::unique_ptr<Y, Deleter>::pointer兼容 T* 那么此重载不参与重载决议。 如果 r.get() 是空指针,那么此重载等价于默认构造函数 (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 - 要共享所有权或从它获得所有权的另一智能指针

异常

3) 无法获得要求的额外内存时会抛出 std::bad_alloc。可能因其他错误抛出实现定义的异常。当发生异常时,如果 T 不是数组类型,那么就(C++17 起)会调用 delete ptr,否则会调用 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 是否拥有作为 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 起要求此测试。

裸指针重载认定被指向对象的所有权。从而,为已经由某个 shared_ptr 管理的对象用裸指针重载构造 shared_ptr,例如以 shared_ptr(ptr.get()),很可能导致未定义行为,即使对象有派生自 std::enable_shared_from_this 的类型也是如此。

因为默认构造函数是 constexpr 的,所以静态 shared_ptr 的初始化是在任何动态初始化开始前,作为静态非局部初始化的一部分进行的。这使得在任何静态对象的构造函数中使用 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_ptrunique_ptr 获得它的删除器(std::default_delete<T[]> 对象),所以能正确解分配数组。C++17 中不再允许这样做。应当代之以使用数组形式 std::shared_ptr<T[]>

示例

#include <iostream>
#include <memory>

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 << "从函数对象中调用 delete。Foo::id=" << p->id << '\n';
        delete p;
    }
};

int main()
{
    {
        std::cout << "1) 无管理对象的构造函数\n";
        std::shared_ptr<Foo> sh1;
    }
    
    {
        std::cout << "2) 有对象的构造函数\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) 有对象和删除器的构造函数\n";
        std::shared_ptr<Foo> sh4(new Foo{11}, D());
        std::shared_ptr<Foo> sh5(new Foo{12}, [](auto p)
        {
            std::cout << "从 lambda 中调用 delete... p->id=" << p->id << '\n';
            delete p;
        });
    }
}

输出:

1) 无管理对象的构造函数
2) 有对象的构造函数
Foo::Foo(10)
sh2.use_count(): 1
sh2.use_count(): 2
sh3.use_count(): 2
Foo::~Foo(),id=10
3) 有对象和删除器的构造函数
Foo::Foo(11)
Foo::Foo(12)
从 lambda 中调用 delete... p->id=12
Foo::~Foo(),id=12
从函数对象中调用 delete。Foo::id=11
Foo::~Foo(),id=11

缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

缺陷报告 应用于 出版时的行为 正确行为
LWG 3548 C++11 源自 unique_ptr 的构造函数复制构造删除器 改为移动构造

参阅

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