Effective C++读书笔记之十三:以对象管理资源

Item 13:Use objects to manage resources

假设我们使用一个用来塑膜投资行为的程序库,其中各式各样的投资类型继承自一个root class:

class Investment { ... };  //“投资类型”继承体系中的root class

进一步假设,这个程序系通过一个工厂函数(工厂函数会“返回一个base class指针,指向新生成的derived class 对象),供应我们某特定的Investment对象:

Investment* createInvestment(  );  

createInvestment的调用端使用了函数返回的对象后,有责任将其删除。现在考虑有个f函数履行了这个责任:

void f()
{
	Investment* pInv = createInvestment(); //调用factory函数
	...
	delete pInv;   //释放pInv所指对象
}

这看起来妥当,但若干情况下f可能无法删除它得自createInvestment的投资对象——或许因为”。。。“区域内的一个过早的return语句,又或者这个delete语句可能位于某循环内,而该循环由于某个continue或goto语句过早退出。无论delete如何被略过去,我们泄漏的不只是内含投资对象的那块内存,还包括那些投资对象所保存的任何资源。

而且一旦软件开始接受维护,可能会有人添加return语句或continue语句而未能全然领悟它对函数的资源管理策略造成的后果。因此单纯依赖”f总是会执行其delete语句是行不通的“。

为了解决这个问题,作者推荐了auto_ptr和shared_ptr两种智能指针。

auto_ptr是个”类指针对象“,其析构函数自动对其所指对象调用delete。下面师范如何使用:

void f()
{
	std::auto_ptr<Investment> pInv(createInvestment());
	//调用factory函数
	...
}  //经由auto_ptr的析构函数自动删除pInv

所谓“以对象管理资源”的观念常被称为“资源取得时机便是初始化时机”(即RAII),因为我们几乎总是在获得一笔资源后于同一语句内以它初始化某个管理对象。

//云风评注:使用auto_ptr时一定要小心。它解决的是在栈堆上使用指针引用一个对象后的安全释放问题。多个auto_ptr不能共享同一对象的所有权,也不能通过它引用一个对象数组等。auto_ptr有诸多问题,现在已经不推荐使用。在新的标准中,建议使用unique_ptr.//

auto_ptr有一个不同寻常的性质:若通过copy构造函数或copy assignment操作符复制它们,它们会变成NULL,而复制所得的指针将取得资源的唯一拥有权。

std::auto_ptr<Investment> pInv1(createInvestment()); 

std::auto_ptr<Investment> pInv2(pInv1);//现在pinv2指向对象,pInv1被设为NULL

pInv1 = pInv2; //现在pInv1指向对象,pInv2被设为NULL

auto_ptr的替代方案是”引用技术型智慧指针“(RCSP)。所谓RCSP也是个智能指针,持续追踪共有多少对象指向某笔资源,并在无人指向它时自动删除该资源。然而RCSP无法打破环状引用,例如两个其实已经没被使用的对象彼此互指,因而好像还处在”被使用“状态。

最后,auto_ptr和tr1::shared_ptr两者都在其析构函数内做delete而不是delete[]动作。那意味在动态分配而得的array身上使用auto_ptr或tr1::shared_ptr是个馊主意。可叹的是,那么做仍能通过编译。

std::auto_ptr<std::string> aps(new std::string[10]);

std::tr1::shared_ptr<int> spi(new int[1024]);

这两种方式都会用上错误的delete形式。

//云风评注:[ ]和vector是C++混乱的根源之一。如果你决定使用STL或template风格的C++,那么尽可能避免使用[ ],而不要去纠结哪个效率更高。

请注意:

为防止资源泄漏,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源。

Effective C++读书笔记之十三:以对象管理资源

时间: 2024-11-13 21:12:20

Effective C++读书笔记之十三:以对象管理资源的相关文章

effective C++ 读书笔记 条款14 以对象管理资源

如果我们使用一个投资行为的程序库: #include "stdafx.h" #include <iostream> #include <memory> using namespace std; class Investment { public: }; class InvestmentFactory { public: virtual Investment* createInvestment() { Investment * inV = NULL; return

Effective Java读书笔记(3对于所有对象都通用的方法)

3.1 覆盖equals时请遵守通用约定 什么时候应该覆盖Object.equals()方法呢? 如果类具有自己特有的"逻辑相等"概念(不同于对象等同的概念),而且超类还没有覆盖equals以实现期望的行为,这时我们就需要覆盖equals方法. Object.equals()方法具有自反性.对称性.传递性.一致性和与null比较返回false的特点. 实现高质量equals方法的诀窍: (1)使用==操作符检查"参数是否为这个对象的引用".如果是,则返回true,这

Effective Java2读书笔记-创建和销毁对象(一)

第1条:考虑用静态工厂方法代替构造器 通常情况下,我们创建一个对象采取new的形式,但是还有一种方法也是经常使用到的,它的名称叫做静态工厂方法. 例如,java中基本类型boolean的包装类Boolean就采用了这种方式,源代码如下: public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); } 当然,除了valueOf这种比较low的名字之外,我们常用的还有getInstance(最常见),newInstanc

Effective Java2读书笔记-创建和销毁对象(三)

第5条:避免创建不必要的对象 本条主要讲的是一些反面教材,希望大家引以为鉴. ①无意中使用自动装箱导致多创建对象. public class Sum { public static void main(String[] args) { Long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } System.out.println(sum); } } sum被声明为Long而不是long,意味着每次i都要被自

Effective Java2读书笔记-创建和销毁对象(二)

第3条:用私有构造器或者枚举类型强化Singleton属性 这一条,总体来说,就是讲了一个小技巧,将构造器声明为private,可以实现单例.具体有以下几种实现的方式. ①最传统的单例实现模式,可能有很多变种,核心思想是私有化构造器. public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton(){}; public static Singleton g

Effective Java2读书笔记-创建和销毁对象(四)

第7条:避免使用终结方法 这一条讲的简直是不知所云.先简单记下来其中说出的几条: ①显式终止方法的典型例子有InputStream.OutputStream和java.sql.Connection上的close方法,以及java.util.Timer上的cancel方法.这些方法一般与try-catch连用,在finally中调用显式的终止方法.终结方法的意义在于这些close方法忘记调用时,充当安全网的作用(感觉扯淡,close都会忘,终结方法能记得?). ②finalize方法,源自Obje

Effective Java 读书笔记(2创建和销毁对象)

第一章是引言,所以这里不做笔记,总结一下书中第一章的主要内容是向我们解释了这本书所做的事情:指导Java程序员如何编写出清晰.正确.可用.健壮.灵活和可维护的程序. 2.1考虑用静态工厂方法代替构造器 静态工厂方法与构造器相比有四大优势: (1)静态工厂方法有名称,具有适当名称的静态工厂方法易于使用.易于阅读: (2)不必每次在调用它们的时候都创建一个新的对象: (3)可以返回原返回类型的任何子类型的对象: (4)在创建参数化类型实例的时候,它们使代码变得更加简洁. 同时静态工厂方法也有两大缺点

Effective C++读书笔记之十二:复制对象时勿忘其每一个成分

Item 12:Copy all parts of an object 如果你声明自己的copying函数,意思就是告诉编译器你并不喜欢缺省显示中的某些行为.而编译器会对"你自己写出copying函数"做出一种复仇的行为:既然你拒绝它们为你写出copying函数,如果你的代码不完全,它们也不会告诉你.结论很明显:如果你为class添加一个成员变量,你必须同时修改copying函数.如果你忘记,编译器不太可能提醒你. 一下提供一种正确的模版: class Date{...}; class

Effective Objective-C 读书笔记

一本不错的书,给出了52条建议来优化程序的性能,对初学者有不错的指导作用,但是对高级阶段的程序员可能帮助不是很大.这里贴出部分笔记: 第2条: 使用#improt导入头文件会把头文件的内容全部暴露到目标文件中,而且如果两个类之间存在循环引用则会出现编译错误,所以要尽量使用@class进行类声明. 如果需要实现一个协议,则必须#improt这个协议的头文件,所以可以将协议单独定义在一个.h文件当中.如果这个协议是代理模式协议的一部分,即需要与类捆绑使用才有实际意义,则建议定义在类当中,并以类名为前