13 14 15 16 17对象管理资源

13 以对象管理资源

一、对象管理资源(智能指针)

  • 获得资源后立刻放进管理对象内
    以对象管理资源的观念常被称为“资源取得的时机便是初始化时机(Resource Acquistion Is Initialization;RAII)”
  • 管理函数运用析构确保资源被释放。

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

并非所有的资源都是heap-based,如Mutex,需要lock和unlock操作。此时你需要编写自己的资源管理类。需要注意copying 函数的行为。
当你自己编写的资源管理类发生复制时,泥坑采取的操作:

  • 禁止复制
    许多时候允许RAII对象复制并不合理,此时可以考虑禁止复制。
  • 对底层资源使用引用计数法
    有时希望保有资源,直到它的最后一个使用者被销毁。这是可以使用shared_ptr。如:
class Lock{
public:
explict Lock(Mutex* pm)
: mutexPtr(pm,unlock)
{
    lock(mutexPtr.get());
}
private:
std::tr1::shared_ptr<Mutex> mutexPtr;
};

通过对shared_ptr指定删除器,在析构的时候调用unlock。

  • 复制底部资源
    如果你希望针对一份资源拥有任意数量的副本,那么复制底部资源时要进行深拷贝
  • 转移底部资源的拥有权
    如果希望确保永远只有一个RAII对象指向一个未加工的资源,请将资源的拥有权从被复制物转移到目标物

总结

复制RAII对象必须一并复制它所管理的资源,所以资源的copying行为局定RAII对象的copying行为。
普遍而常见的RAII class copying行为是:抑制copying 、实施引用计数法。


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

一、获取原始资源的方法

  • 显式转换
//C  API
//FontHandle getFong();
//void releaseFont(FongHandle fh);

Class Font{
public:
explicit Font(FontHandle fn)
:f(fh)
{}
~Font(){releaseFont(f);}

FontHandle get() const { return f; } //显示转换函数

private:
FontHandle f;
};
  • 隐式转换
Class Font{
public:
explicit Font(FontHandle fn)
:f(fh)
{}
~Font(){releaseFont(f);}

operator FontHandle() const { // 隐式转换函数
    return f;
}

private:
FontHandle f;
};

//隐式转换示例 changeFontSize为C API
Font f(getFont());
int newFontSize;
...
changeFontSize(f,newFontSize);//将Font隐式转换为FontHandle

隐式转换会增加发生错误的机会。客户可能在需要Font的时候意外获得了FontHandle。如:

Font f1(getFont());
...
Font f2 = f1;//原意是要copy一个Font对象,却反而将f1隐式转换为其底部的FontHandle然后才复制它

二、总结

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

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

new -> delete
new type[] -> delete []

typedef std::string AddressLines[4]; //每个人的地址有4行,每行是一个string
std::string* pal = new AddressLines; //new AddressLines返回一个string*,就像 new string[4] 一样

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

以独立语句将newed 对象存储于智能指针内。如果不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄漏。

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

编译器产出一个processWidget调用码前,必须先核算即将被传递的各个实参。
第一个实参由两部份组成:

  • 执行new Widget 表达式
  • 调用tr1::shared_ptr构造函数
    在调用processWidget之前,编译器必须创建代码做以下三件事:
  • 调用priority
  • 执行new Widget 表达式
  • 调用tr1::shared_ptr构造函数
    new 表达式一定执行在tr1::shared_ptr之前,若调用顺序为:
    1.执行new Widget 表达式
    2.调用priority
    3.调用tr1::shared_ptr构造函数
    则调用priority发生异常,那么new Widget返回的指针会遗失。从而引发资源泄漏。按如下方式可避免:
std::tr1::shared_ptr<Widget> pw(new Widget);
processWidget(pw,priority());

以上方式可行的原因是:编译器对跨越语句的各项操作没有重新排列的自由。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值