本文内容为《C++ Primer(第五版)》相关章节整理而得。
- 为了更容易。更安全的使用动态内存,新的标准库提供了以下智能指针类型来管理动态对象,它的行为与常规指针类似,重要的区别是它负责自动释放所指向的类型。
shared_ptr:允许多个指针指向同一个对象。unique_ptr:独占所指向的对象。weak_ptr:弱引用,指向shared_ptr所管理的对象。
- 这三种类型都定义在
memory头文件中。 - 在早期C++中,还有一个
auto_ptr智能指针,在C++98中引入,由于有缺陷,在C++11中被标记为弃用,在C++17中彻底移除,官方建议用unique_ptr替代。
一、shared_ptr类※
1. 定义※
创建一个智能指针时,必须提供指针可以指向的类型,例如:
shared_ptr<string> p1; /// 指向string
shared_ptr<list<int>> p2; /// 指向int的list默认初始化的智能指针中保存着一个空指针。
2. 智能指针支持的操作※
| 操作 | 解释 | 适用指针 |
| shared_ptr<T> sp | 空智能指针,指向类型为T的对象 | |
| unique_ptr<T> up | ||
| p | 将p用作一个条件判断,若p指向一个对象,则为true | 两个指针都适用 |
| *p | 解引用指针,获得所指向对象 | |
| p→mem | 等价于(*p).mem | |
| p.get() | 返回p中保存的指针,若智能指针释放了其对象,返回的指针所指向的对象也就消失了。 | |
| swap(p,q) | 交换p和q中的指针 | |
| p.swap(q) | ||
| make_shared<T>(args) | 返回一个shared_ptr,指向一个动态分配的类型为T的对象。使用args初始化此对象。 | shared_ptr独有 |
| shared_ptr<T>p (q) | p是shared_ptr的拷贝,此操作会递增q中的计数器,q中的指针必须能转换为T* | |
| p = q | p和q都是shared_ptr,所保存的指针必须能相互转换。此操作会递减p的引用计数,递增q的引用计数;若p的引用计数变为0,则将其管理的原内存释放。 | |
| p.unique() | 若p.use_count()为1,返回true,否则返回false。 | |
| p.use_count() | 返回与p共享对象的智能指针数量,可能很慢,主要用于调试。 |
3. make_shared函数※
- 这是最安全的分配和使用动态内存的方法,次函数在动态内存中分配一个对象并初始化它,但会指向此对象的
shared_ptr。 - 定义在头文件
memory中。
shared_ptr<int> p3 = make_shared<int>(42); /// 指向一个值为42的int的shared_ptr
shared_ptr<int> p4 = make_shared<int>(); /// 值默认为0- 如果不传递任何参数,则对象会进行值初始化。
4. shared_ptr的拷贝和赋值※
当进行拷贝或赋值操作时,每个
shared_ptr都会记录有多少个其他shared_ptr指向相同的对象,例如:auto p = make_shared<int>(42); /// p指向的对象只有p一个引用者。 auto q(p); /// p和q指向相同的对象,此对象有两个引用者。- 引用计数:每个
shared_ptr都有一个关联的计数器。- 当
shared_ptr被拷贝时,计数器递增。 - 当
shared_ptr被赋予新值或者销毁时,计数器递减。 - 当计数器为0时,释放所管理对象。
- 当
5. 定义和改变shared_ptr的其他方法※
| 操作 | 解释 |
| shared_ptr<T> p(q) | p管理内置指针q所指向的对象,q必须指向new分配的内存,且能够转换为T*类型。 |
| shared_ptr<T> p(u) | p从unique_ptr u接管了对象的所有权,将u置为空 |
| shared_ptr<T> p(q, d) | p接管了内置指针q所指向的对象的所有权。q必须能转换为T*类型。p将使用可调用对象d来代替delete |
| shared_ptr<T> p(p2, d) | p是shared_ptr p2的拷贝,区别是p将用可调用对象d来代替delete |
| p.reset() | 若p是唯一指向其对象的shared_ptr,reset会释放此对象。若传递了可选的参数内置指针q,会令p指向q,否则会将p置为空。若还传递了参数d,将会调用d而不是delete来释放q。 |
| p.reset(q) | |
| p.reset(q, d) |
6. 智能指针使用基本规范※
- 不要混合使用智能指针和普通指针。
- 不使用相同的内置指针初始化(或reset)多个智能指针
- 不delete get()返回的指针。
- 不使用get()初始化或reset另一个智能指针。
- 如果使用get()返回的指针,记住当最后一个对应的智能指针销毁后,所返回的指针就变为无效了。
- 如果使用智能指针管理的资源不是new分配的内存,需要传递给它一个删除器。
二、unique_ptr类※
- 一个
unique_ptr拥有它所指向的对象,与shared_ptr不用,某个时刻只能有一个unique_ptr指向一个给定对象。
1. 定义※
与
shared_ptr不同,没有类似make_shared的标准库函数来返回一个unique_ptr,定义一个unique_ptr时,需将其绑定到一个new 返回的指针上,且初始化unique_ptr时必须采用直接值初始化形式:unique_ptr<double> p1; /// 可以指向一个double的unique_ptr unique_ptr<int> p2(new int(42)); /// p2指向一个值为42的int- 由于一个
unique_ptr拥有它所指向的对象,因此它不支持普通的拷贝或赋值操作。
2. unique_ptr支持的操作※
- 除了第一节第2点中介绍的智能指针都支持的操作之外,
unique_ptr还支持下列操作:
| 操作 | 解释 |
| unique_ptr<T> u1 | 空unique_ptr,可以指向类型为T的对象。u1会使用delete来释放它的指针,u2会使用一个类型为D的可调用对象来释放它的指针。 |
| unique_ptr<T, D> u2 | |
| unique_ptr<T, D> u(d) | 空unique_ptr,指向类型为T的对象,用类型D的对象代替delete。 |
| u = nullptr | 释放u指向的对象,将u置空。 |
| u.release() | u放弃对指针的控制权,返回指针,并将u置空。 |
| u.reset() | 释放u指向的对象,如果提供了内置指针q,则令u指向q。否则将u置空。 |
| u.reset(q) | |
| u.reset(nullptr) |
- 虽然不能拷贝或者赋值
unique_ptr,但可以通过调用release或reset将指针的所有权从一个(非const)unique_ptr转移给另一个unique_ptr。
三、weak_ptr类※
weak_ptr是一种不控制所指向对象生存期的智能指针,它指向一个由shared_ptr管理的对象,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
1. 定义※
auto p = make_shared<int>(42);
weak_ptr<int> wp(p); /// wp弱共享p,p的引用计数未改变。由于对象可能不存在,因此不能使用
weak_ptr直接访问对象,而必须调用lock。if (shared_ptr<int> np = wp.lock()) {} /// 如果np不为空则条件成立
2. weak_ptr支持的操作※
| 操作 | 解释 |
| weak_ptr<T> w | 空weak_ptr可以指向类型为T的对象 |
| weak_ptr<T> w(sp) | 与shared_ptr sp指向相同对象的weak_ptr。T必须能转换为sp指向的类型。 |
| w = p | p可以是一个shared_ptr或一个weak_ptr。赋值后w与p共享对象。 |
| w.reset() | 将w置为空。 |
| w.use_count() | 与w共享对象的shared_ptr的数量 |
| w.expired() | 若w.use_count()为0,则返回true,否则返回false |
| w.lock() | 如果expired为true,返回一个空shared_ptr,否则返回一个指向w的对象的shared_ptr |