13:以对象管理资源
- 资源获取即初始化(RAII):使用析构函数确保资源被释放
- 复制时使用移动语义,移交资源的所有权
- 背景:使用动态内存分配时,很容易忘记delete,尤其是程序在中间退出(比如if判断后return)
- RAII(Resource Acquisition Is Initialization)资源获取即初始化:
- 资源的有效期与持有资源的对象的生命周期严格绑定(即获取资源的时候要通过构造函数初始化)
- 对象独占资源
- 即让编译器在每个退出的分支上,对象都进行析构,从而释放资源
- 使用模板更加方便
- 移交所有权
- 背景:如果两个指针同时指向一个资源,会析构两遍;因此RAII类独占资源(类似
unique_ptr
) - 在RAII类中,将拷贝相关的函数设置为
=delete
,RAII无法进行拷贝 - 因此只能通过移动构造函数使用
std::move
进行移交所有权
- 背景:如果两个指针同时指向一个资源,会析构两遍;因此RAII类独占资源(类似
- 如何把RAII类作为函数的参数
- 值传递:各位caller,我不要ownership了,请拿走
- 非const引用传递:拿不拿走都行,提前商量好(不推荐)
- const引用传递:可以拿走用一下,但是ownership还是我的
- 右值引用:同第二条,无法确定caller是否拿走了ownership
- C++98与C++11
- C++98中
std::auto_ptr
类似于C++11中std::unique_ptr
,但是std::unique_ptr
不允许所有权被转移 - C++98中
std::tr1::shared_ptr
类似于C++11中std::shared_ptr
,weak_ptr
只是拥有资源的使用权而非所有权,因此不占用引用计数,可以解决环状引用的问题
- C++98中
- 梳理:RAII作为一种管理资源的方式(或思想),早期使用
auto_ptr
作为解决方案,C++11之后使用unique_ptr和move语义作为解决方案 - 参考:
14:在资源管理类中小心copying行为
复制RAII对象必须一并处理资源的copy行为
- copy行为的不同情况:
- 大部分情况下,对RAII对象的复制操作本身就不合法
- 对底层资源使用引用计数法(
shared_ptr
) - 复制底层资源(行为像值,进行深拷贝)
- 转移资源所有权(
unique_ptr
)
15:在资源管理类中提供对原始资源的访问
- 将RAII对象转换为对资源的直接访问
- 通过显示转换:提供一个
get()
函数返回智能指针内部的原始指针 - 通过隐式转换
- 像使用原始指针一样使用智能指针,比如智能指针一样可以使用
->
访问成员 - 直接访问原始指针:在RAII类内实现返回原始指针的类型转换运算符
- 像使用原始指针一样使用智能指针,比如智能指针一样可以使用
- 通过显示转换:提供一个
16:成对使用new和delete时要采取相同形式
- new一个对象,使用delete释放;new一个数组,使用
delete []
进行释放delete []
表示知道释放的是数组,读取数组元素数量,从而多次调用析构函数
- 尽量避免对数组使用
typedef
,此时在delete时很容易出现混淆:用delete还是delete[]
,可以的话可以使用std::vector
等容器
17:以独立语句将new的对象置入智能指针
- 背景:编译器可能对单一语句中的执行顺序进行重新调整
1 2 3 4 5 6 7 8 9 10
int priority() {} void func(std::shared_ptr<MyResource> sp, int priority) {} func(std::shared_ptr<MyResource>(new MyResource), priority()); /* 该语句的执行顺序可能是: MyResource* tmp_ptr = new MyResource; int priority = priority(); std::shared_ptr<MyResource> sp = std::shared_ptr<MyResource>(tmp_ptr); */
- 如果
int priority = priority();
执行失败,则tmp_ptr
指向的临时资源无法被释放,发生内存泄漏 - 根本原因是:资源被创建和资源被转换成资源管理对象有时间差,中间可能有干扰
- 如果
- 解决方法:以独立语句将new的对象置于智能指针中,因为编译器无法对跨语句的操作进行调整
1 2
std::shared_ptr<MyResource> sp(new MyResource); func(sp, priority());