那么 C++ 真的不能自动回收垃圾吗?带着这个疑问我们来看看一般 C++ 程序都是怎样回收资源的。
void function(
{
int *p = new int; // 申请内存
// 资源申请下来了,不玩有个 p 用?
// do something
delete p; // 释放内存
}
这段示例代码在 function( 函数开始的时候申请了一块内存,大小对应于 int 类型,然后在函数结束的时候释放它。通常来说,这看起来很OK,没毛病,但是,如果遇到了下面几种情况呢?
- 程序如果中途有逻辑让它提前退出 function( 函数
- 发生了异常而没有被捕获到
RAII 是什么
众所周知 C++ 具有面向对象的特性,在初始化类对象的时候,系统会调用类构造函数。如果类对象是存放在栈空间的话,比如声明为局部变量,那么当类对象超出生命周期时,比如退出局部变量的作用域,系统会调用这个对象的类析构函数;如果类对象是存放在堆空间的话,比如通过 new 操作符创建的类对象,那么当类对象被销毁时,比如对对象执行 delete 操作,系统同样会调用类析构函数。
modern C++ 实践建议优先把资源存放在栈上。如果只是个变量类型,完全可以用局部变量的形式定义声明,这样代码块在退出后系统自动回收栈上的资源。
void function(
{
// 声明定义为局部变量,资源存储在栈区
int data = 0;
// do something with data
// 函数退出时,自动释放 data 占用的空间
}
当资源比较占空间时,需要在堆上分配资源,可以通过指针引用它,资源的申请放在类的构造函数里,然后在析构函数里释放。下面举个例子
class Helper
{
private:
int* data;
public:
Helper( {
data = new int; // 在堆上申请内存
}
~Helper( {
delete data; // 释放堆上申请的内存
}
void do_something_with_data( {}
};
void function(
{
// 声明定义为局部变量,对象存储在栈区
// 调用 Helper 类构造函数在堆上申请资源
Helper help;
// 通过对象 help 调用成员 data
// 如果 data 是 Helper 私有成员
// 在类外面必须通过类成员方法调用 data
help.do_something_with_data(;
// 函数退出时,自动释放 help 对象占用的栈空间
// 就算发生了异常或者中途退出都会执行这一步
// help 对象被销毁时,调用 Helper 类析构函数
// Helper 类析构函数释放已申请的堆上资源
}
利用这种特性的行为被 C++ 发明人称呼为 RAII,英文全称是「resource acquisition is initialization」,中文翻译过来是「资源获取即是初始化」。而我喜欢把它叫做上下文管理,实现资源申请释放的类叫做上下文管理器(context manager。
经典实践--智能指针
unique_ptr 属于 Smart Points 中的一种,Smart Points 在国内通常翻译为「智能指针」。智能指针负责管理和释放资源。上面的 function( 函数可以改成这样子
#include <memory>
void function(
{
// 实例化智能指针对象,输入需要被管理的内存首地址
// 对象为局部变量,存储在栈区
std::unique_ptr<int> data(new int;
// 智能指针对象就像普通指针一样调用
printf("data=%d\n", *data;
// 函数退出时,自动释放 data 对象占用的栈空间
// 就算发生了异常或者中途退出都会执行这一步
// data 对象被销毁时,同步释放被管理的内存资源
}
可见,用了智能指针后,不需要像之前那样定义类 Helper (上下文管理器了,代码清爽很多。
有的,C++ 14 之后标准库添加了 make_unique,演示一下怎么用
std::unique_ptr<int> data = std::make_unique<int>(;
荐书活动
编程的设计思想是一门很有意思的事情,其中有一门前人总结得很到位的学问叫「设计模式」,想深入了解吗?
图书简介:
本书共23章,包括认识设计模式、单例模式、工厂模式、建造者模式、原型模式、适配器模式、装饰器模式、外观模式、桥接模式、组合模式、享元模式、代理模式、策略模式、命令模式、状态模式、模板方法模式、备忘录模式、中介者模式、观察者模式、迭代器模式、责任链模式、访问者模式、解释器模式。
通过以上的知识,让你从模式小白直接升级为模式大神!本书所需源代码,均可通过本书配套下载链接获得。 本书适合编程初学者或希望在面向对象编程上有所提高的开发人员阅读。