Effective C++——资源管理

C++ 中的资源类型:1.最为常见也最为熟知的是内存资源 2.文件描述器 3.互斥锁 4.数据库连接 5. socket 6.GUI 中 的字型、笔刷等

在不再使用这些资源时,确保释放掉这些资源是必须的。但现实往往很难,由于异常出现函数内多重回传路径程序后期维护者未能理解前者的意图导致的维护异常等

一、以对象管理资源

1.定义一个类,class Person{};
2.Person* CreatePerson();工厂函数
3.void EmployPerson()
{
Person *p = CreatePerson();
//TODO
delete p;
}
这个程序看起来没有任何问题,但一下情况都会导致问题出现
a)delete 语句之前若是有return 语句执行,则程序会提前退出,而执行不了释放资源的delete操作
b)delete p 语句要是存在于某个循环语句中,也可能在break,continue,goto 语句过早退出。
c)delete p之前出现异常,导致执行不到delete 语句
d)后期维护时,可能会加入return 或goto 等导致资源没法释放的情景。
总之,将资源释放的任务交给EmployPerson函数很不靠谱!
为确保资源被释放,应该将资源放入对象中,从而利用C++ 自动调用析构函数的机制。

标准程序库中的智能指针特别适用于那种资源分配在heap上,而后被应用在单一区块或函数内的情形。以下是用auto_ptr改写的EmployPerson函数

void EmployPerson()
{
    std::auto_ptr<Person>  p(CreatePerson());
    //TODO  
}//auto_ptr的析构函数自动删除p

总结:以对象管理资源的两个关键点
1.获得资源后立即放进管理对象内
2.管理对象运用析构函数释放资源

auto_ptr特性介绍:多次delete 同一指针会出错,为确保不会有多个auto_ptr指向同一对象,auto_ptr有个特性——通过copy 构造函数或copy = 操作符复制对象,复制所得的指针将会取得唯一拥有权,被复制的对象会变为null。
这个诡异的特性导致STL容器容不得auto_ptr

auto_ptr的替代方案是”引用计数型智慧指针”(RCSP),RCSP也是个智能指针,持续追踪共有多少对象指向某笔资源,并在无人指向它时自动删除。
不足之处:RCSP无法打破环状引用,例如两个其实已经没被使用的对象彼此互指,因而好像还处在被使用状态。
优点:STL容器所需的正常复制行为可以满足
tr1::shared_ptr就是RCSP的一种,例子如下

void EmployPerson()
{
    std::tr1::shared_ptr<Person>  sp1(CreatePerson());//引用计数为1
    std::tr1::shared_ptr<Person>  sp2(sp1);//引用计数为2
    sp1 = sp2;//引用计数为2
    //TODO  
}

注:auto_ptr 和tr1::shared_ptr两者的析构函数内做的都是delete 动作而不是delete[] 动作。

二、Heap-based资源可以交给智能指针处理,但很多资源并不是这种类型的,此时智能指针就不适合当资源管理者了,需要设计自己的资源管理类。

#include <iostream>
#include <mutex>
using namespace std;

class ResManager
{
public:
    explicit ResManager(mutex * mx) :mutex_(mx)
    {
        mutex_->lock();
    }
    ~ResManager()
    {
        mutex_->unlock();
    }

private:
    mutex *mutex_;

};
int main()
{
    mutex m;
    //TODO:建立一个区块用来定义critical section
    {
        ResManager res(&m);
        // .... 执行critical section内的操作

    }//自动解除互斥器锁定

    cin.get();
}

说明:类的设计需要遵守RAII 守则,即在构造函数中初始化,在析构函数中释放资源。
下面提出一个问题:如果一个RAII对象被复制,会发生什么?
选择有两种:1.禁止复制 (继承Uncopyable 类,参考前面文章)
2.对底层资源使用”引用计数法”,tr1::shared_ptr便是如此。通常只要一个RAII类包含一个tr1::shared_ptr类成员变量,RAII类便有了reference-count copying 功能。
这里有个问题需要先考虑,tr1::shared_ptr默认行为是删除资源,而这里的需求是释放资源。庆幸的是tr1::shared_ptr允许指定一个deleter ,它是一个函数或函数对象。当引用计数为0时自动调用它,顺便说一句,auto_ptr没有这个功能,它会直接删除资源

class ResManager
{
public:
    explicit ResManager(mutex * mx) :mutex_(mx,unlock)
    {
        mutex_->lock();
    }
private:
    std::tr1::shared_ptr <mutex> mutex_;

};

注:这里没有声明析构函数的必要性了,因为ResManager类默认的析构函数会调用tr1::shared_ptr对象mutex_的析构函数。而mutex_的析构函数会在引用计数为0时调用deleter 所指函数unlock

三、将new 对象独立地置于智能指针

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

考虑调用processWidget函数,如下:
processWidget(new Widget,priority());tr1::shared_ptr构造函数是个explicit修饰的,不接受隐式转换

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

以上调用有可能会导致资源泄露,为什么?

调用priority(),new Widget,tr1::shared_ptr构造函数,C++ 编译器以什么样的顺序来调用不好确定。
考虑如果new Widget之后,先是调用priority()函数,再调用tr1::shared_ptr构造函数。此时如果priority()函数调用出现异常,明显会出现资源泄露。
解决方案:

std::tr1::shared_ptr<Widget> sp(new Widget);
processWidget(sp,priority());//这里不会发生资源泄露,因为编译器在跨语句的时候没有自由去排列实参的调用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值