13用对象管理资源
- 为防止资源泄露,使用RAII(资源获取时机就是初始化时机)对象,在构造函数中获得资源,析构函数中释放资源
- 使用uniuqe_ptr or shared_ptr 文中提到的auto_ptr 在c++11中被废弃了
考虑在以下代码中
void foo(){
base* B = factory(); //获取到资源
...
delete B; //释放资源
}
若在…中出现异常抛出等问题,那么delete B就不是一定被执行,造成内存泄漏,因此考虑使用对象进行管理,在程序离开作用域时,对象就会自动被析构
void foo(){
share_ptr<Base> shp(factory()); //获取到资源
...
}
14在资源管理类中小心copying行为
考虑以下代码,使用一个Lock类对互斥锁mutex进行管理
class Lock{
public:
explicit Lock(Mutex* pm):mutexPtr(pm){
lock(mutexPtr);
}
~Lock(){
unlock(mutexPtr);
}
private:
Mutex* mutexPtr;
}
看起来非常合理,但是如果发生copy会怎么样?
Mutex m;
Lock m11(&m);
Lock m12(m11);
以下几种方式:
- 禁止复制,如条款6将copy操作声明为private
- 使用‘引用计数法’,使用shared_ptr,指定删除器,因为如果shared_ptr引用为0时,会默认将资源删除,如果是锁一类的资源,可以指定删除器函数为unlock,在引用为0时调用指定删除器
- 对资源进行深拷贝
- 前两条更普遍
15在资源管理类中提供对原始资源的访问
- 有两种方式,提供显示转换或者隐式转换,其中显示转换更安全,隐式转换对客户比较方便
考虑一个使用于字体资源的RAII类,由于经常需要访问原始资源Fonthandle,需要经常将Font转换为Fontheadle,
Fonthandle getFont(); //简化参数,获取一个Fonthandle
void releaseFont(Fonthandle fh);
class Font{
public:
explicit Font(Fonthandle fh):f(fh){}
Fonthandle get(){ return f;}//提供显示转换函数
operator FontHandle() const{ return f;}//隐式转换函数
~Font(){releaseFont(f);}
private:
FontHandle f;
}
void changeFontSize(Fonthandle fh, int newSize);
Font f(getFont());
int newSize;
changeFontSize(f.get(),newSize)//显示转换
changeFontSize(f,newSize)//隐示转换
16 成对使用new和delete要保证形式相同
- 许多编译器实现数组和单个对象的内存分布,通常是在数组的前几位bit存储,此数组有多少个对象,因此如果对数组指针使用delete,以及对普通对象指针使用delete [] ,都是未定义行为
- 不要对数组使用typedef。
17 以独立语句将newed对象放入智能指针
int someOper();
voif processWidget(shared_ptr<Widget> pw, int someOper());
processWidget(shared_ptr<Widget>(new Widget), someOper());//1.不推荐
shared_ptr<Widget> pw(new Widget);//2.
processWidget( pw, someOper());//3.
- 不推荐1处做法 共完成三件事
- 执行new Widget
- 调用shared_ptr构造函数
- someOper()
- 对于这三件事的执行顺序没有明确定义,但是1一定在2后面,如果出现执行顺序是,132,且3抛出异常了,那么就会出现资源泄露,因此推荐将new对象和放入智能指针分开写。