Item13:以对象管理资源
-
熟记:任何向系统申请得来的资源,不再使用它们时,必须将它们还给系统。
-
不能倚靠程序员会谨慎写程序释放资源。如下:尽管在f()函数有delete pInv,但是如果程序中间有控制流return / goto,而未delete pInv,那么就会导致资源泄漏。
void f() { Investment* pInv = createInvestment(); ... delete pInv; }
-
解决办法:使用标准程序库提供的“智能指针”auto_ptr,其析构函数自动对其所指对象调用delete。
std::auto_ptr pInv(createInvestment());
这个例子表明“以对象管理资源”的两个关键想法:- 获得资源后立刻放进管理对象:即“资源取得时机便是初始化时机”(Resource Acquisition Is Initialization; RAII)
- 管理对象运用析构函数确保资源被释放:不管控制流如何离开区块,一旦对象被销毁(例如当对象离开作用域),其析构函数自然会被自动调用。
-
“智能指针”auto_ptr的性质:若通过copy构造函数或copy assignment操作符复制它们,它们会变成null,而复制所得的指针将取得资源的唯一拥有权!【防止多个auto_ptr指向同一对象,当对象被删除一次后,其他指针会发生“未定义行为”】
std::auto_ptr<Investment> pInv1(createInvestment()); // pInv1会变为null std::auto_ptr<Investment> pInv2(pInv1); // pInv2会变为null pInv1 = pInv2;
-
也可以使用shared_ptr。
-
shared_ptr是一种“引用技术型智慧指针”(reference-counting smart pointer; RCSP)。其特点是:持续跟踪共有多少对象指向某笔资源,并在无人指向它时自动删除该资源。【类似垃圾回收,但无法打破环状引用】
std::tr1::shared_ptr<Investment> pInv1(createInvestment());
// pInv1和pInv2指向同一对象
std::tr1::shared_ptr<Investment> pInv2(pInv1);
pInv1 = pInv2;
Item16:成对使用new和delete时要采取相同形式
-
错误示例:
stringArray是一个指向数组的指针,当使用delete时,只调用一次析构函数释放一个string对象【string[0]】,其余99个对象未被释放。string* stringArray = new string[100]; delete stringArray;
-
正确规则:
如果new时使用[],则delete时也使用[];否则,不使用[]。string* stringPtr1 = new string; string* stringPtr2 = new string[100]; delete stringPtr1; // 删除一个对象 delete[] stringPtr1; // 删除一个由对象组成的数组
-
要避免的示例:【结论:最好不要对数组形式做typedefs动作,而应使用vector< string >】
typedef string AddressLines[4]; // AddressLines是地址有4行,每行是一个string的数据类型 string* pal = new AddressLines; // new AddressLines返回一个string*,就像new string[4] 一样 delete pal; // 行为未有定义 delete[] pal; // 应该使用delete[] 删除
Item17:以独立语句将newed对象置入智能指针
-
若调用如下函数:
// 函数声明 int priority(); void processWidget(std::tr1::shared_ptr<Widget> pw, int priority); // 这个语句无法通过编译。tr1::shared_ptr构造函数需要一个原始指针(raw pointer),但该构造函数是个explicit构造函数,无法进行因式 processWidget(new Widget, priority()); //而应写成 processWidget(std::tr1::shared_ptr<Widget> (new Widget), priority());
- 执行顺序:【“new Widget”一定执行于std::tr1::shared_ptr之前,priority()可以排在第一或第二或第三执行】假设按照如下顺序调用:
- (1) 执行"new Widget";
- (2) 调用priority
- (3) 调用std::tr1::shared_ptr构造函数
- 问题:如果 调用priority 的过程出现异常,那么new Widget返回的指针将会遗失,造成资源泄漏。
- 正确作法:【使用分离语句】
std::tr1::shared_ptr<Widget> pw(new Widget); processWidget(pw, priority());
- 执行顺序:【“new Widget”一定执行于std::tr1::shared_ptr之前,priority()可以排在第一或第二或第三执行】假设按照如下顺序调用:
-
请记住:
- 以独立语句将newed对象存储于智能指针内。否则,一旦异常抛出,有可能导致难以察觉的资源泄漏。