模板类之智能指针

智能指针

1. 定义

行为类似于指针的类对象,但还有其他功能。

智能指针能够帮助管理动态内存分配

要创建智能指针对象,必须包含头文件 memory

有三类:auto_ptr , unique_ptr , shared_ptr 。其中 auto_ptr 已经被 C++11 摒弃。

2. 注意事项

为什么会有三种智能指针?

三种智能指针可以处理一个问题,就是两个指针对象赋值后的内存释放问题:两个指针同时指向一个空间,那释放内存时同一块空间将被释放两次。

三种处理方式:

  • 定义运算符,进行深拷贝
  • 建立所有权(ownership),只有一个指针对象能够拥有这块内存空间,赋值操作后所有权转让。这是 auto_ptr , unique_ptr 的策略
  • 创建智能更高的指针,跟踪引用这个对象的智能指针数量,称为引用计数(reference counting) 。例如赋值时,count+1,指针过期 delete 时,count-1。这是 shared_ptr 的策略

3. unique_ptr 为何优于 auto_ptr

unique_ptr 会在编译阶段就报错, auto_ptr 则会在运行阶段使程序崩溃

这样一看,在编译阶段就报错的操作更好。

unique_ptr 是怎么解决呢,当把一个临时右值赋给智能指针对象时,赋值操作将合法。因为临时右值将会在复制后快速被销毁,就不会造成 指针悬挂

unique_ptr 还有一个有点。他有一个可用于数组的变体:

  • delete 和 delete[] 配对
  • new 和 new[] 配对

auto_ptr 则没有。

4. 如何选择

shared_ptr

如果程序要使用多个指向同一个对象的指 针,应选择 shared_ptr。例子:

  • 有一个指针数组,并使用一 些辅助指针来标识特定的元素,如最大的元素和最小的元素;
  • 两个对象 包含都指向第三个对象的指针;
  • STL 容器包含指针。

很多 STL 算法都支 持复制和赋值操作,这些操作可用于 shared_ptr,但不能用于 unique_ptr(编译器发出警告)和 auto_ptr(行为不确定)。如果您的编 译器没有提供 shared_ptr,可使用 Boost 库提供的 shared_ptr。

unique_ptr

  • 如果程序不需要多个指向同一个对象的指针,则可使用 unique_ptr。
  • 如果函数使用 new 分配内存,并返回指向该内存的指针,将其返回类型声明为 unique_ptr 是不错的选择。这样,所有权将转让给接受返回值的 unique_ptr,而该智能指针将负责调用 delete。
  • 可将 unique_ptr 存储到 STL 容器中,只要不调用将一个 unique_ptr 复制或赋给另一个的 法或算法(如 sort( ))。

在满足 unique_ptr 要求的条件时,也可使用 auto_ptr,但 unique_ptr 是 更好的选择。如果您的编译器没有提供 unique_ptr,可考虑使用 BOOST 库提供的 scoped_ptr,它与 unique_ptr 类似。

weak_ptr

1
2
3
4
5
6
7
weak_ptr<T> w;	 	//创建空 weak_ptr,可以指向类型为 T 的对象
weak_ptr<T> w(sp); //与 shared_ptr 指向相同的对象,shared_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,否则返回非空 shared_ptr

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include < assert.h>

#include <iostream>
#include <memory>
#include <string>

using namespace std;

int main() {
shared_ptr<int> sp(new int(10));
assert(sp.use_count() == 1);
weak_ptr<int> wp(sp); // 从 shared_ptr 创建 weak_ptr
assert(wp.use_count() == 1);
if (!wp.expired()) { // 判断 weak_ptr 观察的对象是否失效
shared_ptr<int> sp2 = wp.lock(); // 获得一个 shared_ptr
*sp2 = 100;
assert(wp.use_count() == 2);
}
assert(wp.use_count() == 1);
cout << "int:" << *sp << endl;
return 0;
}
作者

Erial

发布于

2022-09-18

更新于

2023-02-20

许可协议

评论