std::shared_ptr

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

std::shared_ptr 是通过指针保持对象共享所有权的智能指针。多个 shared_ptr 对象可占有同一对象。下列情况之一出现时销毁对象并解分配其内存:

delete 表达式或在构造期间提供给 shared_ptr 的定制删除器销毁对象。

shared_ptr 能在存储指向一个对象的指针时共享另一对象的所有权。此特性能用于在占有其所属对象时,指向成员对象。存储的指针为 get() 、解引用及比较运算符所访问。被管理指针是在 use_count 抵达零时传递给删除器者。

shared_ptr 亦可不占有对象,该情况下称它为空 (empty) (空 shared_ptr 可拥有非空存储指针,若以别名使用构造函数创建它)。

shared_ptr 的所有特化满足可复制构造 (CopyConstructible) 、可复制赋值 (CopyAssignable) 和可比较小于 (LessThanComparable) 的要求并可按语境转换bool

多个线程能在 shared_ptr 的不同实例上调用所有成员函数(包含复制构造函数与复制赋值)而不附加同步,即使这些实例是副本,且共享同一对象的所有权。若多个执行线程访问同一 shared_ptr 而不同步,且任一线程使用 shared_ptr 的非 const 成员函数,则将出现数据竞争;原子函数的 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所指对象的引用计数
(公开成员函数)
(弃用)
检查所管理对象是否仅由当前 shared_ptr 的实例管理
(公开成员函数)
检查是否有关联的管理对象
(公开成员函数)
提供基于拥有者的共享指针排序
(公开成员函数)

非成员函数

创建管理新对象的共享指针
(函数模板)
创建共享指针,管理用分配器分配的新对象
(函数模板)
应用 static_castdynamic_castconst_castreinterpret_cast 到被存储指针
(函数模板)
返回指定类型的删除器,如果拥有的话
(函数模板)
与另一个 shared_ptrnullptr 进行比较
(函数模板)
将存储的指针的值输出到输出流
(函数模板)
特化 std::swap 算法
(函数模板)
特化的原子操作
(函数模板)

帮助类

std::shared_ptr 的哈希支持
(类模板特化)

推导指引(C++17 起)

注意

只能通过复制构造或复制赋值其值给另一 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 可以从指向不完整类型的裸指针构造)。

实现说明

在典型的实现中, std::shared_ptr 只保有二个指针:

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

以调用 std::make_sharedstd::allocate_shared 创建 shared_ptr 时,以单次分配创建控制块和被管理对象。被管理对象在控制块的数据成员中原位构造。通过 shared_ptr 构造函数之一创建 shared_ptr 时,被管理对象和控制块必须分离分配。此情况中,控制块存储指向被管理对象的指针。

shared_ptr 持有的指针是通过 get() 返回的;而控制块所持有的指针/对象则是最终引用计数归零时会被删除的那个。两者并不一定相等。

shared_ptr 的析构函数会将控制块中的 shared_ptr 计数器减一,如果减至零,控制块就会调用被管理对象的析构函数。但控制块本身直到 std::weak_ptr 计数器同样归零时才会释放。

实践的实现中,若有指向同一控制块的共享指针,则弱指针数量可自增

为满足线程安全要求,引用计数器典型地用等价于用 std::memory_order_relaxedstd::atomic::fetch_add 自增(自减要求强异常顺序,以安全销毁控制块)。

示例

#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex>
 
struct Base
{
    Base() { std::cout << "  Base::Base()\n"; }
    // 注意:此处非虚析构函数 OK
    ~Base() { std::cout << "  Base::~Base()\n"; }
};
 
struct Derived: public Base
{
    Derived() { std::cout << "  Derived::Derived()\n"; }
    ~Derived() { std::cout << "  Derived::~Derived()\n"; }
};
 
void thr(std::shared_ptr<Base> p)
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::shared_ptr<Base> lp = p; // 线程安全,虽然自增共享的 use_count
    {
        static std::mutex io_mutex;
        std::lock_guard<std::mutex> lk(io_mutex);
        std::cout << "local pointer in a thread:\n"
                  << "  lp.get() = " << lp.get()
                  << ", lp.use_count() = " << lp.use_count() << '\n';
    }
}
 
int main()
{
    std::shared_ptr<Base> p = std::make_shared<Derived>();
 
    std::cout << "Created a shared Derived (as a pointer to Base)\n"
              << "  p.get() = " << p.get()
              << ", p.use_count() = " << p.use_count() << '\n';
    std::thread t1(thr, p), t2(thr, p), t3(thr, p);
    p.reset(); // 从 main 释放所有权
    std::cout << "Shared ownership between 3 threads and released\n"
              << "ownership from main:\n"
              << "  p.get() = " << p.get()
              << ", p.use_count() = " << p.use_count() << '\n';
    t1.join(); t2.join(); t3.join();
    std::cout << "All threads completed, the last one deleted Derived\n";
}

可能的输出:

Base::Base()
  Derived::Derived()
Created a shared Derived (as a pointer to Base)
  p.get() = 0xc99028, p.use_count() = 1
Shared ownership between 3 threads and released
ownership from main:
  p.get() = (nil), p.use_count() = 0
local pointer in a thread:
  lp.get() = 0xc99028, lp.use_count() = 3
local pointer in a thread:
  lp.get() = 0xc99028, lp.use_count() = 4
local pointer in a thread:
  lp.get() = 0xc99028, lp.use_count() = 2
  Derived::~Derived()
  Base::~Base()
All threads completed, the last one deleted Derived

版本历史

  • (当前 | 先前 2017年12月11日 (一) 06:32223.72.68.230讨论. . (7,977字节) (0). . (撤销)
  • 当前 | 先前 2017年9月3日 (日) 22:42Fruderica讨论 | 贡献 . . (7,977字节) (+2,400). . (撤销)
  • 当前 | 先前 2016年3月17日 (四) 23:29P12讨论 | 贡献. . (5,577字节) (-36). . (rm orig english text) (撤销)
  • 当前 | 先前 2016年2月4日 (四) 23:28Wizardforcel讨论 | 贡献 . . (5,613字节) (0). . (撤销)
  • 当前 | 先前 2016年2月4日 (四) 23:27Wizardforcel讨论 | 贡献. . (5,613字节) (+2,164). . (撤销)
  • 当前 | 先前 2013年7月2日 (二) 09:06P12bot讨论 | 贡献 . . (3,449字节) (-366). . (Use {{lc}}. Update links. Various fixes.) (撤销)
  • 当前 | 先前 2012年11月16日 (五) 01:59Timothyqiu讨论 | 贡献. . (3,815字节) (-1,841). . (人工翻译) (撤销)
  • 当前 | 先前 2012年11月2日 (五) 15:13P12bot讨论 | 贡献 . . (5,656字节) (+233). . (r2.7.3) (机器人添加:de, en, es, fr, it, ja, pt, ru) (撤销)
  • 当前 | 先前 2012年10月27日 (六) 03:40P12讨论 | 贡献 . . (5,423字节) (0). . (1个修订: Translate from the English version) (撤销)
  • 当前 | 先前 2012年10月26日 (五) 06:00TranslationBot讨论 | 贡献. . (5,423字节) (-125). . (Translated from the English version using Google Translate) (撤销)
  • 当前 | 先前 2012年10月25日 (四) 14:55P12讨论 | 贡献 . . (5,548字节) (0). . (1个修订: Translate from the English version) (撤销)
  • 当前 | 先前) 2012年10月25日 (四) 12:00TranslationBot讨论 | 贡献. . (5,548字节) (+5,548). . (Translated from the English version using Google Translate)