摘自:《Effetive C++》中文版第三版
1.以对象管理资源
为什么要管理资源?
因为所谓资源就是一旦用了它将来必须还给系统。因此将资源放进对象内,用该对象的析构函数可以达到自动释放资源的目的。智能指针auto_ptr就是一个很好的例子。
考虑一个函数
class PointerObject{
public:
#返回一个该类的指针对象
PointerObject* createObject(){...}
}
void func()
{
auto_ptr<PointerObject> pStr(createObject());
}
由此得到以对象管理资源的两个关键想法
- 获得资源后立刻放进对象中
- 对象运用析构函数确保资源被释放
注意:若auto_ptr管理的资源必须绝对没有另外一个auto_ptr指向它。这意味着如果连续两次让一个资源被auto_ptr初始化,那么在对第二个auto_ptr进行初始化时会将第一个auto_ptr置为NULL。若不然,则会导致两个auto_ptr析构同一个资源,引起double_free错误。因此我们在c++11中引入了shared_ptr,可以持续追踪共有多少对象指向某个资源,并且在无人指向它时自动删除该资源。
2.在资源管理类中的注意事项
类的拷贝
思考一个互斥锁
class Lock{
public:
explicit Lock(Mutex* pm):mutex_(pm){
lock(mutex_)
}
~Lock(){ unlock(mutex); }
}
这样便能确保对一个互斥量进行加锁后一定会在对应代码区块的尾部执行解锁操作,而避免死锁。但是如同Lock这样的类是不能拷贝的。
提供对原始资源的访问
其实就是用shared_ptr或者auto_ptr时,例如shared_ptr< Object > pOb; 此时pOb的类型并不是Object*,但是shared_ptr提供一个get成员函数,可以返回被管理的对象的原始指针类型。
void func(Object* p){
}
...
func(pOb.get());
3.成对使用new和delete时要采取相同形式
思考如下代码
string* str = new string[100];
delete str;
delete str时只能删除一个str对象,但是在这片内存区域上有100个string对象,所以正确的方法应当是delete [] str。
关键:
当使用new时:
- 内存被分配出来(通过名为operator new的函数);
- 在这片内存区域上调用一个或多个构造函数。
string* str = new string[100]显然是在申请的堆空间中多次调用了string的构造函数。
当使用delete时:
- 在这片内存区域上调用一个或多个构造函数;
- 释放这片内存(通过名为operator delete的函数)。
delete str时会产生太少的析构函数被调用的问题,导致结果为未定义的状态。
因此,在new表达式中使用[],必须在相应delete表达式中使用[]。4.以独立语句将newed对象置入智能指针
其实就是利用单独语句为shared_ptr赋值
std::shared_ptr<Object> pOb(new Object);
书上列举了一种情况,例如函数
int priority();
void func(shared_ptr<Object>, int priority);
如果以
func(shared_ptr<Object>(new Object), priority());
方式传入参数的话,编译器对
- new object
- priority()
- shared_ptr构造函数的调用
这三个操作执行次序是未知的,如果在调用priority时产生异常,new object已经执行了malloc函数,但是之后并没有放入shared_ptr中,也就是没有被正确地析构,那么这将会导致内存泄漏。