Effect C++ 学习笔记三:资源管理

资源:内存,文件描述器,互斥锁,图形界面的字体和笔刷,数据库链接,socket等。

十三:Use objects to manage resources. 以对象管理资源

class Investment{...};

Investment* createInvestment();

void f()

{
   Investment* pInv = createInvestment();

   //...

   delete pInv;

}

扫描二维码关注公众号,回复: 9536924 查看本文章

为了确保createInvestment返回的资源总是被释放,我们需要将资源放进对象内,当控制流离开f,该对象的析构函数会自动释放那些资源。把资源放进对象内,我们便可依赖c++的“析构函数自动调用机制”确保资源被释放。

改写f():

void f()

{
    std::auto_ptr<Investment> pInv(createInvestment());

    ...

   //调用factory函数 一如以往地使用pInv 经由auto_ptr地析构函数自动删除pInv

}

注:获得资源后立刻放进管理对象内。 管理对象运用析构函数确保资源被释放。

然而auto_ptr的所有权模型表明同时只能有一个auto指针指向对象。

std::auto_ptr<Investment> pInv1(createInvestment());//pInv1指向createInvestment返回物

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

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

可以用引用计数模型指针替代它。

总结:

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

两个常被使用的RAII classes分别是tr1::shared_ptr和auto_ptr。前者通常时较佳选择,因为其copy行为比较直观。若选择

auto_ptr,复制动作会使它指向null。

十四:Think carefully about copying behaviour in resource-managing classes。在资源管理类中小心coping行为

void lock(Mutex* pm);
void unlock(Mutex* pm);

class Lock
{
public:
	explicit Lock(Mutex* pm)
		:mutexPtr(pm)
	{
		lock(mutexPtr);//获得资源
	}
	~Lock() { unlock(mutexPtr); }//释放资源
private:
	Mutex *mutexPtr;
};

//客户对Lock的用法符合RAII方式

Mutex m; //定义所需的互斥器
...
{        //建立一个区块用来定义critical section
	Lock m1(&m);//锁定互斥器
	...        //执行critical section内的操作
}              //在区块最末尾, 自动解除互斥器锁定

如果复制了Lock对象。

Lock m11(&m);   //锁定m

Lock m12(m11); //复制

当一个RAII对象被复制,我们可以选择

1.禁止复制。 见前 private copying

2.引用计数。

我们可以改变mutexPtr的类型,用shared_ptr封装起来,但shared_ptr的缺省行为是“当引用计数为0时删除其所指物。”

我们可以给shared_ptr指定一个删除器(function),当引用计数为0时被调用。

见例子:

class Lock
{
public:
	explicit Lock(Mutex* pm)
		:mutexPtr(pm, unlock)
	{
		lock(mutexPtr.get());
	}
private:
	std::tr1::shared_ptr<Mutex> mutexPtr;
};

3.复制底部资源。进行deep copy.

4.转移底部资源的所有权。

总结:

1.复制RAII对象必须一并复制它所管理的资源,所以资源的copying行为决定RAII对象的copying行为。

2.普遍而常见的RAII class copying 行为是:抑制    copying   引用计数。

十五:Provide access to raw resources in resource-managing classes.在资源管理类中提供对原始资源的访问

例如tr1::shared_ptr和auto_ptr提供一个get成员函数,用来执行显式转换,它会返回智能指针内部的原始指针

注:APIs往往要求访问原始资源,所以每一个RAII class应该提供一个“取得其所管理之资源”的方法。

对原始资源的访问可能由显式转换或隐式转换。一般来说显示转换比较安全,但隐式转换对客户比较方便。

十六:Use the same form in corresponding uses of new and delete 成对使用new和delete时要采用相同形式

	std::string* stringArray = new std::string[100];

	delete stringArray;

使用new 时 1.分配内存 2.构造函数被调用  使用delete时 1.析构函数被调用  2.内存被释放

delete执行时需要知道删除的那个指针所指的是单一对象或对象数组,决定它执行析构的数量。

通过显式声明 delete[]告诉程序释放的是一个数组结构。

结论:

new[]   和 delete[]对应

new和delete对应。

需要注意的是typedef 需要清楚以何种方式释放。

十七:Store newed objects in smart pointers in standalone statements.以独立语句将newed对象置入智能指针。

考虑一个例子:

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

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

考虑这个调用,

编译器可能的操作顺序:

1.调用priority

2.执行"new Widget"

3.调用tr1::shared_ptr构造函数

也有可能是这样的操作顺序:

1.执行 "new Widget"

2.调用 priority

3.调用 tr1::shared_ptr 构造函数

在这种调用顺序下,如果对priority的调用导致异常,会导致资源泄露。

为了避免这个风险,考虑:

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

processWidget(pw, PRIORITY());

这样写可以规定顺序,防止风险。

总结:

以独立语句将newed对象存储于智能指针内。

发布了35 篇原创文章 · 获赞 5 · 访问量 411

猜你喜欢

转载自blog.csdn.net/qq_33776188/article/details/103065067