Namespaces
Variants

std:: shared_ptr

From cppreference.net
Memory management library
( exposition only* )
Allocators
Uninitialized memory algorithms
Constrained uninitialized memory algorithms
Memory resources
Uninitialized storage (until C++20)
( until C++20* )
( until C++20* )
( until C++20* )

Garbage collector support (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
定义于头文件 <memory>
template < class T > class shared_ptr ;
(C++11 起)

std::shared_ptr 是一种通过指针保留对象共享所有权的智能指针。多个 shared_ptr 对象可以拥有同一对象。当以下任一情况发生时,该对象将被销毁且其内存被释放:

  • 最后一个拥有该对象的 shared_ptr 被销毁;
  • 最后一个拥有该对象的 shared_ptr 通过 operator= reset() 被赋予另一个指针。

该对象通过 delete表达式 或在构造时提供给 shared_ptr 的自定义删除器进行销毁。

一个 shared_ptr 可以在共享对象所有权的同时存储指向另一个对象的指针。此特性可用于在拥有所属对象的同时指向其成员对象。存储指针是通过 get() 、解引用及比较运算符访问的指针。托管指针是在使用计数归零时传递给删除器的指针。

一个 shared_ptr 也可以不拥有任何对象,这种情况下它被称为 的(若使用别名构造函数创建,空的 shared_ptr 可能具有非空的存储指针)。

所有 shared_ptr 的特化均满足 CopyConstructible CopyAssignable LessThanComparable 的要求,并且可 上下文转换 bool 类型。

所有成员函数(包括拷贝构造函数和拷贝赋值运算符)均可由不同线程在不同 shared_ptr 对象上调用,无需额外同步,即使这些对象是副本且共享同一对象的所有权。若多个执行线程在无同步情况下访问同一 shared_ptr 对象,且任一访问涉及 shared_ptr 的非 const 成员函数,则会出现数据竞争;此时可使用 std::atomic<shared_ptr> 来避免数据竞争。

目录

成员类型

成员类型 定义
element_type
T (C++17 前)
std:: remove_extent_t < T > (C++17 起)
weak_type (C++17 起) std:: weak_ptr < T >

成员函数

构造新的 shared_ptr
(公开成员函数)
若无更多 shared_ptr 指向被管理对象则将其销毁
(公开成员函数)
shared_ptr 赋值
(公开成员函数)
修改器
替换被管理对象
(公开成员函数)
交换被管理对象
(公开成员函数)
观察器
返回存储的指针
(公开成员函数)
解引用存储的指针
(公开成员函数)
(C++17)
提供对被存储数组的带下标访问
(公开成员函数)
返回指向同一被管理对象的 shared_ptr 对象数量
(公开成员函数)
(until C++20)
检查被管理对象是否仅由当前 shared_ptr 对象管理
(公开成员函数)
检查存储的指针是否非空
(公开成员函数)
提供基于所有者的共享指针排序
(公开成员函数)
(C++26)
提供基于所有者的共享指针散列
(公开成员函数)
提供基于所有者的共享指针相等比较
(公开成员函数)

非成员函数

创建管理新对象的共享指针
(函数模板)
创建使用分配器分配新对象的共享指针
(函数模板)
对存储的指针应用 static_cast dynamic_cast const_cast reinterpret_cast
(函数模板)
返回指定类型的删除器(如果拥有)
(函数模板)
(C++20 中移除) (C++20 中移除) (C++20 中移除) (C++20 中移除) (C++20 中移除) (C++20)
与另一个 shared_ptr 或与 nullptr 进行比较
(函数模板)
将存储的指针值输出到输出流
(函数模板)
特化 std::swap 算法
(函数模板)
std::shared_ptr 特化的原子操作
(函数模板)

辅助类

原子共享指针
(类模板特化)
std::shared_ptr 的哈希支持
(类模板特化)

推导指南 (自 C++17 起)

注释

对象的所有权只能通过接收另一个 shared_ptr 对象的构造函数或赋值函数来共享。如果仅使用另一个 shared_ptr 持有的原始底层指针来构造新的 shared_ptr ,这个新的 shared_ptr 将假定没有其他 shared_ptr 实例持有它拥有的对象。这将导致(除非后续发生赋值操作)在析构时对同一对象重复应用删除器。

std::shared_ptr 可用于不完整类型 T 。然而,从原始指针构造的构造函数( template < class Y > shared_ptr ( Y * ) )以及 template < class Y > void reset ( Y * ) 成员函数只能通过指向完整类型的指针调用(注意 std::unique_ptr 可以从指向不完整类型的原始指针构造)。

T std :: shared_ptr < T > 中可以是函数类型:此时它管理的是函数指针而非对象指针。这一特性常被用于确保动态库或插件在任意函数被引用期间保持加载状态:

void del(void(*)()) {}
void fun() {}
int main()
{
    std::shared_ptr<void()> ee(fun, del);
    (*ee)();
}

实现说明

在典型实现中, shared_ptr 仅持有两个指针:

  • 存储的指针(由 get() 返回的指针);
  • 指向 控制块 的指针。

控制块是一个动态分配的对象,它包含:

  • 指向被管理对象的指针或被管理对象本身;
  • 删除器(类型擦除);
  • 分配器(类型擦除);
  • 拥有被管理对象的 shared_ptr 数量;
  • 引用被管理对象的 weak_ptr 数量。

当通过调用 std::make_shared std::allocate_shared 创建 shared_ptr 时,控制块和托管对象的内存会通过单次分配完成。托管对象在控制块的数据成员中原位构造。当通过 shared_ptr 构造函数创建 shared_ptr 时,托管对象和控制块必须分别分配。这种情况下,控制块会存储指向托管对象的指针。

shared_ptr 直接持有的指针是通过 get() 返回的指针,而控制块持有的指针/对象是在共享所有者数量达到零时将被删除的对象。这两个指针不一定相等。

shared_ptr 的析构函数会递减控制块的共享所有者计数。当该计数器归零时,控制块将调用被管理对象的析构函数。控制块自身直到 std::weak_ptr 计数器同样归零时才会释放自身。

在现有实现中,若存在指向同一控制块的共享指针,弱指针数量会递增( [1] , [2] )。

为满足线程安全要求,引用计数器通常使用等效于 std::atomic::fetch_add 的操作进行递增,并采用 std::memory_order_relaxed 内存序(递减操作需要更强的内存序以保证安全销毁控制块)。

示例

#include <chrono>
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
using namespace std::chrono_literals;
struct Base
{
    Base() { std::cout << "Base::Base()\n"; }
    // 注意:此处非虚析构函数是可接受的
    ~Base() { std::cout << "Base::~Base()\n"; }
};
struct Derived : public Base
{
    Derived() { std::cout << "Derived::Derived()\n"; }
    ~Derived() { std::cout << "Derived::~Derived()\n"; }
};
void print(auto rem, std::shared_ptr<Base> const& sp)
{
    std::cout << rem << "\n\tget() = " << sp.get()
              << ", use_count() = " << sp.use_count() << '\n';
}
void thr(std::shared_ptr<Base> p)
{
    std::this_thread::sleep_for(987ms);
    std::shared_ptr<Base> lp = p; // 线程安全,即使共享的 use_count 被递增
    {
        static std::mutex io_mutex;
        std::lock_guard<std::mutex> lk(io_mutex);
        print("线程中的局部指针:", lp);
    }
}
int main()
{
    std::shared_ptr<Base> p = std::make_shared<Derived>();
    print("创建了一个共享的 Derived 对象(作为指向 Base 的指针)", p);
    std::thread t1{thr, p}, t2{thr, p}, t3{thr, p};
    p.reset(); // 从主线程释放所有权
    print("3个线程间共享所有权,且主线程已释放所有权:", p);
    t1.join();
    t2.join();
    t3.join();
    std::cout << "所有线程执行完毕,最后一个线程删除了 Derived 对象。\n";
}

可能的输出:

Base::Base()
Derived::Derived()
创建了一个共享的 Derived 对象(作为指向 Base 的指针)
	get() = 0x118ac30, use_count() = 1
3个线程间共享所有权,且主线程已释放所有权:
	get() = 0, use_count() = 0
线程中的局部指针:
	get() = 0x118ac30, use_count() = 5
线程中的局部指针:
	get() = 0x118ac30, use_count() = 4
线程中的局部指针:
	get() = 0x118ac30, use_count() = 2
Derived::~Derived()
Base::~Base()
所有线程执行完毕,最后一个线程删除了 Derived 对象。

示例

#include <iostream>
#include <memory>
struct MyObj
{
    MyObj() { std::cout << "MyObj constructed\n"; }
    ~MyObj() { std::cout << "MyObj destructed\n"; }
};
struct Container : std::enable_shared_from_this<Container> // 注意:公有继承
{
    std::shared_ptr<MyObj> memberObj;
    void CreateMember() { memberObj = std::make_shared<MyObj>(); }
    std::shared_ptr<MyObj> GetAsMyObj()
    {
        // 为成员使用别名 shared_ptr
        return std::shared_ptr<MyObj>(shared_from_this(), memberObj.get());
    }
};
#define COUT(str) std::cout << '\n' << str << '\n'
#define DEMO(...) std::cout << #__VA_ARGS__ << " = " << __VA_ARGS__ << '\n'
int main()
{
    COUT("Creating shared container");
    std::shared_ptr<Container> cont = std::make_shared<Container>();
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    COUT("Creating member");
    cont->CreateMember();
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    COUT("Creating another shared container");
    std::shared_ptr<Container> cont2 = cont;
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    DEMO(cont2.use_count());
    DEMO(cont2->memberObj.use_count());
    COUT("GetAsMyObj");
    std::shared_ptr<MyObj> myobj1 = cont->GetAsMyObj();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    DEMO(cont2.use_count());
    DEMO(cont2->memberObj.use_count());
    COUT("Copying alias obj");
    std::shared_ptr<MyObj> myobj2 = myobj1;
    DEMO(myobj1.use_count());
    DEMO(myobj2.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    DEMO(cont2.use_count());
    DEMO(cont2->memberObj.use_count());
    COUT("Resetting cont2");
    cont2.reset();
    DEMO(myobj1.use_count());
    DEMO(myobj2.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    COUT("Resetting myobj2");
    myobj2.reset();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    COUT("Resetting cont");
    cont.reset();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
}

输出:

<div class="mw-geshi" dir="ltr" style="

另请参阅

(C++11)
具有唯一对象所有权语义的智能指针
(类模板)
(C++11)
对由 std::shared_ptr 管理的对象的弱引用
(类模板)