c++ 资源管理

effective c++ Item13~17 资源管理-学习整理

概括:

Item 13~17 介绍了c++资源管理方面的内容。这5个Item可以分2类,第1类是标准库提供的智能指针介绍(Item13)和使用细节的讨论(Item17);第2类指导如何设计一个资源管理类(Item14,Item15)

详细来看:

Item 13 建议用对象管理资源防止内存泄露

(1)常见姿势:我们习惯于使用new和delete来申请和释放内存。如下:

class Investment {...}

Investment* createInvestment();

void f()
{
	Investment *pInv = createInvestment();
	...												//use pInv
	delete pInv;									//release object
}

但是这样的姿势存在潜在的问题:程序没有走到delete pInv的地方(提前return,循环里goto了,异常早于delete被catch处理)。这些原因都造成内存泄露,所以这种方式靠不住。

(2)建议姿势:为了确保createInvestment()返回的资源总能被释放,将资源放入类中,让类的析构函数在离开f 被调用时自动释放资源。标准库提供:auto_ptr,shared_ptr可以帮我们做到这一点。

void f()
{
	std::auto_ptr<Investment> pInv(createInvestment());
	...
}

(3)注意点:a.复制语境限制了std::auto_ptr,所以尽量使用std::shared_ptr少用auto_ptr;
b.别在动态分配数组上使用std::auto_ptr或者std::shared_ptr。两者都在其析构函数中做delete操作而不是delete []动作。此外c++没有给动态分配的数组设计类似智能指针这样的东西。

Item 14 自己设计之“在资源管理类中小心copying行为”

哪些资源不适合使用c++标准库提供的智能指针呢?非heap-based资源,例如mutex锁。我们希望在mutex资源上也建立起一个类似智能指针的智能类来管理mutex资源,让其在离开作用域的时候能够自行解锁。

我们按照下边方式使用Lock在这里插入图片描述
设计这个资源管理类的过程中,我们遇到的问题来了:copy行为如何定义
在这里插入图片描述
根据std::auto_ptr和std::shared_ptr,已经提供给我们的两种解决思路:
(1)转移底部资源的拥有权——场合少,使用频率很低
(2)对底层资源采用“引用计数法”——希望保有资源直到最后一个使用者被销毁的场合
这种解决方案一个简洁的方法是让我们设计的这个资源管理类直接“is-implemented-in-terms-of” std::shared_ptr(由std::shared_ptr实现),并且改变std::shared_ptr的第二个参数(删除器)
另外的两种解决思路:
(3)直接禁止复制——复制动作不合理的场合
在这里插入图片描述

(4)复制底部资源(xx)

Item 15 自己设计之“在资源管理类中提供对原始资源的访问”

该问题的场景是:如果我们用别人设计的接口,而这个接口参数是个裸指针,那么我们的资源管理对象就不得不提供一个接口来提供裸资源,否则,像下图的调用方式无法通过编译(传递std::shared_ptr 对象给Investment*)
在这里插入图片描述
在这里插入图片描述
解决这个问题的办法有两种:
(1)给我们设计的资源管理类增加一个get接口,返回智能指针内部的原始指针
(2)隐式转换接口
例如,对于我们设计的类Font,FontHandle是我们需要管理的裸资源
在这里插入图片描述
按照上述两种方式,获取裸资源的方法分别如下:
(1)增加get接口获取裸资源(显示转换)
优点:安全,不容易出错
在这里插入图片描述
(2)为Font提供一个隐式转换到它的FontHandle的转换函数
优点:调用自然而简单
缺点:增加了出错的概率
在这里插入图片描述
缺点的例子:f1被销毁时,字体将被RAII类释放,f2则处于悬挂状态。
在这里插入图片描述

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

这个没什么好说的,new申请的内存用delete释放;new 申请的数组存放的资源,用delete []释放。特别注意的是使用typedef动作。

Item 17 在一个独立的语句中将new出来的对象存入智能指针

该Item旨在告诉我们一些关于std::shared_ptr的细节,使用的过程中我们要注意这些设计的细节,否则会陷入无谓的纠结
(1)std::shared_ptr的构造函数是explicit修饰的。它只能显示构造,不支持隐式转换,所以我们用如下方式来使用就不对

processWidget(new Widget, priority());

虽然我们的processWidget的函数申明如下:

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

编译报错,因为我们的std::shared_ptr<Widget>不支持从Widget* 的原始指针到std::shared_ptr<Widget>的隐式转换。我们必须显示的构造这个智能指针,如下:

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

(2)上述过程只描述了本Item的第一个需要注意的地方,没有解释为什么要用一个独立的语句来做这件事。
原因:c++的编译器看到这个函数,在产出它的调用码前,首先进行参数核算,其顺序和java、c#不同,它不是按照顺序的方式执行的,这个函数的定义,编译器会分成3个步骤执行:
第一步:调用priority
第二步:执行new Widget表达式
第三步:调用std::shared_ptr构造函数
这样好像没有什么问题,但是如果编译器也可能按照这个顺序执行:
第一步:执行new Widget表达式
第二步:调用priority (异常恰巧出现)
第三步:调用std::shared_ptr构造函数
这样就出现问题了,第一步申请的内存还没来得及进入第三步std::shared_ptr的构造过程就出现异常导致资源泄漏。因此调用这个函数的过程中可能引发资源泄漏,建议如下方式:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值