Effective C++(13) 用对象管理资源
问题聚焦:
从这条准则开始,都是关于资源管理的。
资源,一旦用了它,将来必须还给系统。
本条准则,基于对象的资源管理办法,建立在C++的构造函数,析构函数和拷贝函数(拷贝构造函数和重载赋值操作符)的基础上。
Demo: root class Investment:
class Investment { ... }; // 工厂函数 Investment* createInvestment(); // 返回指向一个Investment或者其子类的对象,调用者有责任删除它 // 考虑如下调用 void f() { Investment* pInv = createInvestment(); ...... delete pInv; // 释放pInv所指的对象 }
问题:
当在delete语句调用之前出现return语句或者抛出异常并且该异常被默默地忽略掉时,这个对象便无法被释放。
解决思路:
方案一:类指针对象C++的析构函数自动调用机制
标准程序库提供的 auto_ptr 是个“类指针对象”,也就是所谓的智能指针,其析构函数自动对所指对象调用delete。
void f() { std::auto_ptr<Investment> pInv(createInvestment(( )); ...... }
关键:
获得资源后立刻放进管理对象:资源取得时机便是初始化时机管理对象运用析构函数确保资源被释放:一旦对象被销毁(例如当对象离开作用域),其析构函数自然会被自动调用,于是资源被释放。
性质:唯一拥有权。
为了防止多个auto_ptr同时指向同一对象,auto_ptr有一个不同寻常的性质:若通过拷贝构造函数或重载赋值操作符复制它们,原指针会变成null,而复制所得的指针将取得资源的唯一拥有权。
void f() { std::auto_ptr<Investment> pInv(createInvestment(( )); ...... }
方案二:引用计数型智慧指针RCSP
思想:持续追踪共有多少对象指向某笔资源,并在无人指向它时自动删除该资源。
void f() { ... std::str1::shared_ptr<Investment> pInv(createInvestment()); ... }
区别:str1::shared_ptr的调用和auto_ptr相同,区别就是在复制行为上
void f() { ...... std::tr1::shared_ptr<Investment> pInv1(createInvestment()); std::str1::shared_ptr<Investment> pInv2(pInv1); // pInv1和pInv2指向同一对象 pInv1 = pInv2; }
需要格外注意下面这种错误用法:
std::auto_ptr<std::string> aps(new std::string[10]); std::str1::shared_ptr<int> spi(new int[1024]);
错误原因:auto_ptr和str1::shared_ptr两者在其析构函数内做delete而不是delete[]动作。
格外指出:createInvestment返回”未加工指针“是个非常不好的接口设计,在后面的条款中会对这个接口进行修改。
小结:
为了防止资源泄漏,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源两个常被使用的RAIIclasses分别是tr1::shared_ptr和auto_ptr,前者通常是较佳选择,因为其copy行为比较直观。若选择auto_ptr,复制动作会使它指向Null。
参考资料:
《Effective C++ 3rd》