【Effective C++】总结整理之3_资源管理

3.资源管理

资源包括:内存、文件描述符、互斥锁、图形界面中的字型和笔刷、数据库连接、网络socket

基于对象的资源管理办法,建立在C++对构造函数、析构函数、copy函数的基础上.

13.以对象管理资源

  • 把资源放进对象内,我们便可以来C++的“析构函数”自动调用机制确保资源被释放
  • 另一边想法:
void f()
    {
    Investment* pInv=createInvestment();//调用factory函数
    ...
    delete pInv;
}

考虑如上函数f处理Investment对象,会遇到如下问题而导致delete被略去

  • …区域内过早的return
  • delete位于某个循环内,其由于某个continue或goto过早退出
  • …区域内抛出异常

“以对象管理资源”的两个关键想法:

  • RAII(资源取得时机便是初始化时机):每一笔资源都在获得的同时立刻被放进管理对象中
  • 管理对象运用“析构函数”确保资源被释放

auto_ptr——智能指针

auto_ptr有如下特性:

  • 被销毁时自动删除所指之物
  • 若通过copy assignmentcopy赋值它们,则复制所得的指针将取得资源的唯一拥有权
  • 在析构函数内做delete

tr1::shared_ptr——RCSP(reference-counting smart pointer)

特性:

  • 记录指向某笔资源对象的数量,并在无人指向该资源时删除它

  • 无法打破“环状引用

  • 在析构函数内做delete

13总结

  • 为防止资源泄漏,优先使用RAII对象(其在构造函数中获得资源,并在析构函数中释放资源)
  • 注意两种不同指针的copy行为,auto_ptr的copy行为会使原指针指向null;

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

由于并非所有资源都是heap-based,如Mutex,所以需要创建个性化资源管理类

当一个RAII对象被赋值,会发生什么事情?

禁止复制

将copying操作在base class声明为private。

class Lock:private Uncopyable{}; //派生类继承方式

引用计数法——tr1::shared_ptr

tr1::shared_ptr允许指定deleter,其为函数函数对象,当引用次数0时触发。删除器对于tr1::shared_ptr是可有可无的第二参数

class Lock{
public:
	explicit Lock(Mutex* pm)
        :mutexPtr(pm,unlock)   //unlock 为deleter
      	{
			lock(mutexPtr.get());  //锁定原始资源
         }
private:
	std::tr1::shared_ptr<Mutex> mutexPtr;
}
//unlock作为删除器,用以解决“释放”而非删除问题
//缘由shared_ptr默认为删除资源

注意:

  1. Lock不再声明析构函数——class析构函数(不论是编译器生成还是用户自定义的)会自动调用其non-static成员变量的析构函数
  2. mutexPtr的析构函数会在互斥器引用次数0时,自动调用deleter

14总结

  • 赋值RAII对象必须一并复制它所管理的资源,所以资源的copying行为决定RAII对象的copying行为

  • 普遍而常见的RAII class copying行为:

    • 抑制copying(使用Uncopyable作为基类)

    • 引用计数法(tr1::shared::ptr)8

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

std::tr1::shared_ptr<Investment> pInv(createInvestment());//调用factory函数
int daysHeld(const Investment* pi); 					//返回投资天数
int d=daysHeld(PInv)       //错误!!!

显然daysHeld的形参为const Investment*类型,故需RAII对象返回其所内含の原始资源(即Investment*

智能指针

  • 均提供一个成员函数get(),用以返回智能指针内部的原始指针(副本
int d=daysHeld(PInv.get());
  • 几乎均重载了指针取值操作符(operator ->operator *),其允许隐式转换至底部原始指针
class Investment{
public:
	bool isTaxFree() const;
}
Investment* CreateInvestment();   //factory函数生命
std::auto_ptr<Investment*> Ptr1(CreateInvestment());
std::tr1::shared_ptr<Investment*> Ptr2(CreateInvestment());
bool taxable1=Ptr1->isTaxFree();
bool taxable2=(*Ptr2).isTaxFree();

显式隐式获取原始资源

class Font{
    private:
    	FontHandle _f;     //原始资源 raw material
    public:
     	explicit Font(FontHandle f)
            :_f(f)
            {“资源处理”}
    	~Font(){releaseFont(_f)};
    public:
    	//显式转换函数,返回资源副本
    	FontHandle get() const 
        	{return _f;}
    	//隐式转换(Type)函数——转换为其他类型
    	operator FontHandle() const 
        	{return _f;}
}

补充:
隐式转换函数包括两类:

  • 单参数转换构造函数
  • 隐式转换函数(operator Type() const {}) //此为上述代码使用类型

注: 隐转换函数可能发生“虚吊”问题

Font f1(getFont());
...
FontHandle f2=f1;
//此处编译器编译成如下代码
FontHandle f2=FontHandle(f1);

如上代码造成一个问题:
FontHandle有Font对象f1管理,但也可通过直接使用f2取得,当f1被销毁时,字体被释放,f2因此成为*(dangle)虚吊*的。

15总结

  • API往往要求访问原始资源,故每一个RAII CLASS应提供取得其所管理资源的办法
  • 对原始资源的访问包括显示转换隐式转换,一般来说显示转换安全,隐式转换方便

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

### 16总结
  • 尽量不要对数组形式做typedef动作-无法判定以那种形式delete删除之
  • 若使用new[],则对应是哟个delete[]
  • 若使用new,则对应使用delete

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

考虑如下代码·

//函数声明
int priority();
void processWidget(std::tr1::shared_ptr<Widget> pw,int priority());

//函数调用方式1
processWidget(std::tr1::shared_ptr<Widget>(new Widget),priority());

//函数调用方式2
std::tr1::shared_ptr<Widget> Ptr(new Widget);
processWidget(Ptr,priority());

调用方式1

在调用processWidget前,编译器会做如下事项:

  1. 调用priority()
  2. 执行new Widget
  3. 调用tr1::shared_ptr构造函数

由于C++的执行顺序不确定,可以确定的仅有2位与3之前。故可出现如下执行顺序

  1. 执行new Widget
  2. 调用priority()
  3. 调用tr1::shared_ptr构造函数

若在2出现异常,1返回的指针就会丢失,因为它尚未被置入资源管理对象tr1::shared_ptr

简言之

在对processWidget调用过程中可能发生资源泄漏,缘由在资源被创建(new widget资源被转换为资源管理对象的两个时间点之间可能发生异常干扰

故可采取调用方式2避免上述事件发生。

17总结

  • 独立语句newd对象存储与智能指针内。

返回的指针就会丢失,因为它尚未被置入资源管理对象tr1::shared_ptr

简言之

在对processWidget调用过程中可能发生资源泄漏,缘由在资源被创建(new widget资源被转换为资源管理对象的两个时间点之间可能发生异常干扰

故可采取调用方式2避免上述事件发生。

17总结

  • 独立语句newd对象存储与智能指针内。

    若不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄漏

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值