C++资源管理(13-17条款)

13:以对象管理资源

举个例子:

class TeamSys{...}
TeamSys* createTeamSys();//返回指向动态分配的对象TeamSys的指针,调用者有责任删除它。
void f()
{
	TeamSys* ts = createTeamSys();
	...
	delete ts;
}

以上代码中…,可能会出现异常或者return ,会导致ts所指向的对象资源无法被释放。
1.使用auto_ptr可以避免f函数潜在的资源泄露可能性:

void f()
{
	std::auto_ptr<TeamSys> t(createTeamSys());
	...
}
  • 获得资源后立刻放进资源管理对象中
    "以对象管理资源"的观念常被成为“资源取得时机便是初始化实际(RAII)”

  • 管理对象运用析构函数确保资源被释放
    不管控制流如何离开区块,一旦对象被销毁(比如当对象离开作用域)其析构函数自然会被自动调用,资源释放。

auto_ptr被销毁时会自动删除它所指之物。
auto_ptr有一个性质:若通过copy构造函数或copy assignment操作符复制它们,它们就会变成null,而复制所得的指针将取得资源的唯一拥有权。

2.引用计数型智慧指针(reference counting smart pointer RCSP)。

void f()
{
	std::tr1::shared_ptr<TeamSys> ts(createTeamSys());
}

     auto_ptr和tr1::shared_ptr两者都在其析构函数内做delete而不是delete[]动作。所以动态分配而得的array不应该使用这两者。如果你非要这样干,你可以使用boost::scoped_array和boost::shared_array classes。

请记住

  • 为了防止内存泄露,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源。
  • 两个常被使用的RAII classes分别是tr1::shared_ptr和auto_ptr,前者通常是最佳选择,因为其copy行为比较直观。若选择auto_ptr,复制动作会使它指向null。

14:在资源管理类中小心copying行为

class Lock
{
	public:
		explicit Lock(Mutex* pm):mutexPtr(pm){lock(mutexPtr);}
		~Lock(){unlock(mutexPtr);}
	private:
		Mutex *mutexPtr
};
Mutex m;//需要锁定的互斥器
Lock ml1(&m);//锁定
Lock ml2(ml1);//将ml1复制到ml2,会发生什么事?

解决方案:1.禁止复制 2.对底层资源祭出”引用计数“

class Lock
{
	public:
		explicit Lock(Mutex* pm):mutexPtr(pm,unlock){lock(mutexPtr.get());}//unlock 为删除器
	private:
	    tr1::shared_ptr<Mutex> mutexPtr;//不需要析构函数,因为默认系统函数会自动调用其成员变量的析构函数,mutexPtr引用计数为0时,会自动删除器unlock
};

请记住

  • 复制RAII对象必须一并复制它所管理的资源,所以资源的copying行为决定RAII对象的copying行为。
  • 普遍而常见的RAII class copying行为是:抑制copying、施行引用计数法。不过其他行为也都可能被实现。

15:在资源管理类中提供对原始资源的访问

利用资源管理类可以避免资源泄露,但是当你执行下面语句时,

std::tr1::shared_ptr<TeamSys> ts(createTeamSys());
int countsMember(const TeamSys pt);

int counts = countsMember(ts);//错误,因为countsMember需要的是一个TeamSys指针

解决方法:显示转换和隐式转换
1.tr1::shared_ptr和auto_ptr都提供一个get成员函数:countsMember(ts.get()) 返回智能指针内部的原始指针。
2.

FontHandle getFont();
void releaseFont(FontHandle dh);

class Font
{
	public:
		explicit Font(FontHandle fh):f(fh){}
		FonHandle get() const {return f};//显式转换函数
		operator FontHandle()const {return f;}//隐式转换函数
		~Font(){releaseFont(f);}
	private:
		FontHandle f;
};
void changeFontSize(FontHandle f,int newSize);

Font f(getFont());
int new FontSize;
...
changeFontSize(f.get(),newFontSize);显式转换
changeFontSize(f,newFontSize);隐式转换

但是隐式转换会增加错误发生机会,如下:

Font f1(getFont())
...
FontHandle f2 = f1;//原意是想拷贝font对象,现在反而将f1隐式转换为FontHandler,然后才复制它

f1中管理一个FontHandle对象,但是这个FontHandle对象可以直接使用分取得,如果f1不会销毁,f2就是”虚吊的“。

  • 通常显式转换函数如get是比较受欢迎的方法,因为它将”非故意之类型转换“的可能性最小化了。但是隐式转换的”自然用法“好像也不错,怎么说呢?各有优点。

请记住

  • APIs往往要求访问原始资源,所以每一个RAII class 应该提供一个“取得其所管理之资源”的方法。
  • 对原始资源的访问可能经由显式转换或隐式转换,一般而言显式转换比较安全,但是隐式转换对于客户来说比较方便。

16:成对使用new和delete时要采取相同形式

  • 当你对着一个数组指针使用delete,唯一能够让delete知道内存中是否存在一个”数组大小记录“的办法就
string* str1 = new string;
string* str2 = new string[10];

delete str1;
delete [] str2;
  • 尽量不要对数组形式做typedef动作。为了避免你一下错误
typedef string AddressLines[4];

string* pa1 = new AddressLines;//相当于new string[4]

delete pal;//行为未定义
delete [] pal;//正确,调用者容易忘记写[],因为new的时候没有[]

请记住

  • 如果你在new表达式中使用[],必须在相应的delete表达式中也使用[]。如果你在new表达式中不使用[],一定不要在相应的delete表达式中使用[]。

17:以独立语句将newed对象置入智能指针

举个例子:

int priority();
void processWidget(std::tr1::shared_ptr<Widget> pw,int priority);

processWidget(std::tr1::shared_ptr<Widget>(new Widget),priority());

     C++编译器以什么样的顺序执行上面语句的?弹性很大。如果编译器是这样执行的:new Widget=》priority()=》tr1::shared_ptr。这样的话,如果prority()发生异常,Widget对象尚未置入tr1::shared_ptr内,这个指针是资源管理对象,资源没有被转换为资源管理对象,这是错误的。
我们可以通过分离语句的方法避免这类问题:

std::tr1::shared_ptr<Widget> pw(new Widget);
processWidget(pw,prority);

请记住

  • 以独立语句将newed对象存储于(置于)智能指针内。如果不这样,一旦异常被抛出,有可能导致难以察觉的资源泄露。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值