01 shared_ptr 共享的智能指针
shared_ptr 使用引用计数每个 shared_ptr 的拷贝都指向相同的内存,在最后一个 shared_ptr 析构时它指向的内存才会被释放。
shared_ptr 在多线程中线程中的 shared_ptr 共享同一个对象的所有权,若在多个线程中访问同一个 shared_ptr 而不同步,且任意线程使用 shared_ptr 的非 const 成员函数,将会产生数据竞争,使用原子函数的 shared_ptr 特化能用于避免数据竞争(Delete in C++20)。
shared_ptr 在实现上有两个核心的成员:一个是指向资源对象的指针变量,另一个是指向引用计数的指针变量。
shared_ptr 就是通过指向引用计数的指针变量实现实现多个 shared_ptr 对象共享一个 count 计数的目的,即引用计数。
原生的 shared_ptr 在多线程中的读写是不安全的,因为读写操作不具有原子性,所以多线程中使用 shared_ptr 一定要加锁。
-
成员函数
(constructor) // 构造新的 shared_ptr (destructor) // 如果没有更多的 shared_ptr 指向持有的对象则析构对象 operator= // 对 shared_ptr 进行赋值 swap // 交换管理的对象,不会改变引用计数 reset // 替换所管理的对象,原指针指向的对象引用计数减一 get // 返回存储的指针 operator* // 解引用存储的指针 operator-> // 同上 use_count // 返回 shared_ptr 所指对象的引用计数 unique // 检查所管理的对象是否仅由当前的 shared_ptr 管理 get_deleter // 返回指定类型中的删除其(如果有的话) operator bool // 检查是否有被管理的对象 owner_before // 提供基于拥有者的共享指针排序
-
非成员函数
std::make_shared // 创建管理一个对象的共享指针 std::allocate_shared // 创建一个用于分配器分配的对象共享指针 std::static_pointer_cast // 应用对象的显式类型转换到被存储指针 std::dynamic_pointer_cast std::const_pointer_cast std::reinterpret_pointer_cast std::swap(std::shared_ptr) // 特化的 std::swap 算法 relational operators // 在 shared_ptr 对象 lhs 和 rhs 之间或 shared_ptr 和 nullptr 之间执行适当的关系比较操作 ostream operator<< // 输入输出接口
-
注意事项
- 应该优先使用 std::make_shared 构造智能指针,因为它更高效。
- 不能将一个原始指针直接赋值给一个智能指针。
- 不要使用同一个原始指针初始化多个 shared_ptr:
int* p1 = new int(10); std::shared_ptr<int> sp1(p1); // ok std::shared_ptr<int> sp2(p1); // error
- 要避免循环引用,否则会造成内存泄漏(产生了类似死锁互相等待的情况)。
- 线程不安全。
-
应用实例
/*************************************************************** * (constructor) * ************************************************************/ std::shared_ptr<int> sp1; // 默认构造,没有获得的任何指针的所有权,引用计数为 0 std::shared_ptr<int> sp2(nullptr); // 作用同上 std::shared_ptr<int> sp3(new int); // 拥有指向 int 对象指针的所有权,引用计数为 1 std::shared_ptr<int> sp4(new int, std::default_delete<int>()); // 作用同上,但是拥有自己的删除器(析构函数)。主要用于管理对象中包含指针的情况 std::shared_ptr<int> sp5(new int, [](int* p) { delete p; }, std::allocator<int>()); // 作用同上,但是拥有自己的删除器(析构函数)和分配器(构造函数),主要用于管理对象中包含指针的情况 std::shared_ptr<int> sp6(sp5); // 拷贝构造,sp5 的引用计数 + 1 std::shared_ptr<int> sp7(std::move(sp6)); // 移动构造,sp6 的所有权被移交到 sp7 std::shared_ptr<int> sp8(std::unique_ptr<int>(new int)); // sp8 引用计数为 1 /*************************************************************** * operator= * ************************************************************/ std::shared_ptr<int> sp1; std::shared_ptr<int> sp2(new int(10)); sp1 = sp2; // 拷贝赋值 sp2 = std::make_shared<int>(20); // 移动赋值 std::unique_ptr<int> up1(new int(30)); sp1 = std::move(up1); // 将 unique_ptr 移动到 shared_ptr 并设置引用计数为 1 /*************************************************************** * swap * ************************************************************/ std::shared_ptr<int> sp1(new int(10)); std::shared_ptr<int> sp2(new int(20)); sp1.swap(sp2); // 注意此处的 sp1 是对象而不是指针,引用要使用 . 而非 -> /*************************************************************** * reset * ************************************************************/ std::shared_ptr<int> sp1; sp1.reset(new int(20)); // 重置 sp1 管理的对象 /*************************************************************** * get * ************************************************************/ int* p1 = new int(10); std::shared_ptr<int> sp1(p1); std::cout << (p1 == sp1.get()) << std::endl; // 1 /*************************************************************** * use_count * ************************************************************/ std::shared_ptr<int> sp1; std::shared_ptr<int> sp2(new int(10)); std::shared_ptr<int> sp3(new int(20)); std::shared_ptr<int> sp4(sp2); std::cout << sp1.use_count() << std::endl; // 0 std::cout << sp2.use_count() << std::endl; // 2 std::cout << sp3.use_count() << std::endl; // 1 std::cout << sp4.use_count() << std::endl; // 2 /*************************************************************** * unique * ************************************************************/ std::shared_ptr<int> sp1; std::shared_ptr<int> sp2(new int(10)); std::shared_ptr<int> sp3(new int(20)); std::shared_ptr<int> sp4(sp2); std::cout << sp1.unique() << std::endl; // 0 std::cout << sp2.unique() << std::endl; // 0 std::cout << sp3.unique() << std::endl; // 1 std::cout << sp4.unique() << std::endl; // 0 /*************************************************************** * operator bool * ************************************************************/ std::shared_ptr<int> sp1; std::shared_ptr<int> sp2(new int(10)); std::shared_ptr<int> sp3(new int(20)); std::shared_ptr<int> sp4(sp2); std::cout << (sp1 ? "true" : "false") << std::endl; // false std::cout << (sp2 ? "true" : "false") << std::endl; // true std::cout << (sp3 ? "true" : "false") << std::endl; // true std::cout << (sp4 ? "true" : "false") << std::endl; // true /*************************************************************** * relational operators * ************************************************************/ std::shared_ptr<int> sp1, sp2, sp3, sp4; sp1 = std::make_shared<int>(10); sp2 = std::make_shared<int>(10); sp3 = sp2; std::cout << (sp1 == sp2 ? "true" : "false") << std::endl; // false std::cout << (sp2 == sp3 ? "true" : "false") << std::endl; // true std::cout << (sp3 == sp4? "true" : "false") << std::endl; // fasle std::cout << (sp1 == nullptr? "true" : "false") << std::endl; // false std::cout << (sp2 == nullptr ? "true" : "false") << std::endl; // false std::cout << (sp3 == nullptr ? "true" : "false") << std::endl; // false std::cout << (sp4 == nullptr ? "true" : "false") << std::endl; // true /*************************************************************** * ostream operator<< * ************************************************************/ std::shared_ptr<int> sp1(new int(10)); std::cout << sp1 << std::endl; // 0070A590 std::cout << *sp1 << std::endl; // 10 /*************************************************************** * std::make_shared * ************************************************************/ std::shared_ptr<int> sp1 = std::make_shared<int>(10); /*************************************************************** * std::swap * ************************************************************/ std::shared_ptr<int> sp1(new int(10)); std::shared_ptr<int> sp2(new int(20)); std::swap(sp1, sp2); /*************************************************************** * Add in C++17 * std::static_pointer_cast * std::dynamic_pointer_cast * std::const_pointer_cast * std::reinterpret_pointer_cast * ************************************************************/ std::shared_ptr<int> sp1(new int(10)); std::shared_ptr<const int> sp2 = std::const_pointer_cast<const int>(sp1);
-
线程不安全原因
- 一个 shared_ptr 对象实体可被多个线程同时读取(文档例1);
- 两个 shared_ptr 对象实体可以被两个线程同时写入(例2),“析构”算写操作;
- 如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁(例3~5)。
02 unique_ptr 独占的智能指针
unique_ptr 独占一个指针对象指向的内存的所有权:一旦它获得指针指向的内存的所有权,它就会在一定的时刻负责释放指针指向的内存。
unique_ptr 独占内存不需要引用计数所以与 shared_ptr 相比开销较小。
unique_ptr 出于安全的原因不支持拷贝构造、复制运算符重载,只支持移动构造、移动赋值运算符重载。
-
成员函数
(constructor) // 构造新的 shared_ptr (destructor) // 如果没有更多的 shared_ptr 指向持有的对象则析构对象 operator= // 对 unqiue_ptr 进行赋值 swap // 交换管理的对象,不会改变引用计数 reset // 替换所管理的对象,并销毁直至指向的对象 release // 放弃了原本持有的指针指向内存的控制权,返回持有的指针并将持有的指针设置为 nullptr get // 返回存储的指针 get_deleter // 返回指定类型中的删除其(如果有的话) operator bool // 检查是否有被管理的对象
-
非成员函数
std::make_unique // [C++14]创建管理一个对象的 unique_ptr std::swap(std::shared_ptr) // 特化的 std::swap 算法 relational operators // 在 unique_ptr 对象 lhs 和 rhs 之间或 unique_ptr 和 nullptr 之间执行适当的关系比较操作 ostream operator<< // 输入输出接口
-
注意事项
- 不支持拷贝构造、复制运算符重载,只支持移动构造、移动赋值运算符重载。
- 线程安全。
-
应用实例
/*************************************************************** * constructor * ************************************************************/ std::default_delete<int> dd; std::unique_ptr<int> up1; std::unique_ptr<int> up2(nullptr); std::unique_ptr<int> up3(new int); std::unique_ptr<int> up4(new int, dd); std::unique_ptr<int> up5(new int, std::default_delete<int>()); std::unique_ptr<int> up6(std::move(up5)); /*************************************************************** * operator= * ************************************************************/ std::unique_ptr<int> up1; std::unique_ptr<int> up2; std::unique_ptr<int> up3; up1 = std::unique_ptr<int>(new int(10)); up2 = std::move(up1); up3 = std::make_unique<int>(10); // Add in C++14 /*************************************************************** * swap * ************************************************************/ std::unique_ptr<int> up1; std::unique_ptr<int> up2; up1.swap(up2); /*************************************************************** * reset * ************************************************************/ std::unique_ptr<int> up1; up1.reset(new int); std::cout << *up1 << std::endl; // 8975 随机值 up1.reset(new int(10)); std::cout << *up1 << std::endl; // 10 *up1 = 20; std::cout << *up1 << std::endl; // 20 /*************************************************************** * release * ************************************************************/ std::unique_ptr<int> up1(new int(10)); // up1.release(); // error: unique_ptr 放弃了原本持有的指针指向内存的控制权,返回持有的指针并将持有的指针设置为 nullptr auto p1 = up1.release(); std::cout << *p1 << std::endl; // 10 std::cout << (up1.get() == nullptr ? "up1.get == nullptr" : "up1.get == not nullptr") << std::endl; // up1.get == nullptr /*************************************************************** * get * ************************************************************/ int* p1 = new int(10); std::unique_ptr<int> sp1(p1); std::cout << (p1 == sp1.get()) << std::endl; // 1 /*************************************************************** * 以下方法用法同 std::shared_ptr * operator bool * std::make_unique [C++14] * std::swap(std::shared_ptr) * relational operators * ostream operator<< * ************************************************************/
03 weak_ptr 独占的智能指针
weak_ptr 主要是为了解决 shared_ptr 可能引起的循环引用问题。
weak_ptr 对被 shared_ptr 管理的对象具有弱引用,即不会增加 shared_ptr 持有指针的引用计数。
weak_ptr 不能直接使用,在访问所引用的对象前必须先转换成 shared_ptr 类型,如果原来的。
-
成员函数
(constructor) // 构造新的 weak_ptr (destructor) // 如果没有更多的 weak_ptr 指向持有的对象则析构对象 operator= // 对 weak_ptr 进行赋值 swap // 交换管理的对象,不会改变引用计数 reset // 替换所管理的对象,并销毁直至指向的对象 use_count // 返回 weak_ptr 所指对象的引用计数 expired // 返回与 use_count == 0 相同的值 lock // 获取 weak_ptr 监视的 shared_ptr // 如果 weak_ptr 没有过期则返回 shared_ptr // 如果 weak_ptr 过期则返回空的 shared_ptr owner_before //
-
非成员函数
std::swap(std::shared_ptr) // 特化的 std::swap 算法
-
注意事项
-
应用实例
/*************************************************************** * constructor * ************************************************************/ std::shared_ptr<int> sp1(new int(10)); std::weak_ptr<int> wp1; std::weak_ptr<int> wp2(wp1); std::weak_ptr<int> wp3(sp1); std::cout << wp1.use_count() << std::endl; // 0 std::cout << wp2.use_count() << std::endl; // 0 std::cout << wp3.use_count() << std::endl; // 1 /*************************************************************** * operator= * ************************************************************/ std::shared_ptr<int> sp1(new int(10)); std::weak_ptr<int> wp1, wp2; wp1 = sp1; wp2 = wp1; /*************************************************************** * swap * ************************************************************/ std::shared_ptr<int> sp1 (new int(10)); std::shared_ptr<int> sp2 (new int(20)); std::weak_ptr<int> wp1(sp1); std::weak_ptr<int> wp2(sp2); wp1.swap(wp2) /*************************************************************** * reset * ************************************************************/ std::shared_ptr<int> sp1 (new int(10)); std::cout << sp1.get() << std::endl; // 1 std::weak_ptr<int> wp1(sp1); wp1.reset(); std::cout << sp1.get() << std::endl; // 1 /*************************************************************** * use_count * ************************************************************/ std::shared_ptr<int> sp1(new int(10)); std::weak_ptr<int> wp1, wp2; wp1 = sp1; std::cout << wp1.use_count() << std::endl; // 1 std::cout << wp2.use_count() << std::endl; // 0 /*************************************************************** * expired * ************************************************************/ std::shared_ptr<int> sp1(new int(10)); std::weak_ptr<int> wp1 = sp1; std::cout << wp1.expired() << std::endl; // 0 sp1.reset(); std::cout << wp1.expired() << std::endl; // 1 /*************************************************************** * lock * ************************************************************/ std::shared_ptr<int> sp1(new int(10)); std::weak_ptr<int> wp1 = sp1; std::shared_ptr<int> sp2 = wp1.lock(); std::weak_ptr<int> wp2 = sp2; /*************************************************************** * std::swap(std::shared_ptr) * ************************************************************/