effective C++读书笔记(三)

3. 资源管理(Resource Management)

从条款13到条款17都在讲资源管理,那么什么是资源呢?我觉得,资源就是诸如new出来的东西(堆内存),互斥锁啊,数据库的链接等等,操作这些东西要特别小心,因为用了就没有了,比如堆内存,你new了不释放,迟早就是bad_alloc,比如锁,你占了不释放,别人一辈子也得不到。

 

C++的常见资源有动态分配内存、文件描述器、互斥锁、图形界面下的字符和笔刷、数据库连接以及网络的 sockets。不论哪一种资源,当不再使用时,应该将它还给系统。

条款13: 以对象管理资源(useobjects to manage resources)

本条款建议程序员使用对象管理资源(如申请的内存),核心思想就是:把资源放到类里面,将资源释放的活放在类的析构函数中。我们利用的思想就是类的析构函数在对象的生命周期结束,或者有异常的时候自动调用,因此,我们不用担心造成资源的泄露。并且,由条款08知道,析构函数绝不抛异常,因此我们可以放心使用

书上一句经典的话:获得资源后立即放进资源管理对象,我们总是在获得一笔资源后于同一语句内以它初始化某个管理对象。因此资源取得时便是资源管理对象初始化的时候

给出的经验是:

1为防止资源泄漏(常常是由于程序员忘记delete申请的内存,或是由于程序过早的returncontinuegoto语句等等导致),请使用RAII资源取得时机便是初始化时机Resource Acquisition Is Initialization)对象,它们在构造函数中获得资源并在析构函数中释放资源。例如使用类指针对象”auto_ptr

void f(){
 std::auto_ptr<investment> pInv(createInvesment());
 ....        //auto_ptr的析构函数会自动删除pInv
}

2)两个常被使用的RAII classes 分别是:auto_ptrtrl::shared_ptr引用计数型智慧指针reference-counting smart pointerRCSP)。为了防止多个auto_ptr指向同一对象时,对象可能会被删除一次以上而导致错误,auto_ptr复制动作会使它(被复制物)指向null

std::auto_ptr<Investment>pInv1(createInvestment()); //pInv1指向createInvestment()返回物;
     std::
auto_ptr<Investment> pInv2(pInv1);                    //现在pInv2指向对象,而pInv1被设为NULL;
     pInv1 = pInv2;                                                           //
现在pInv1指向对象,而pIn2被设为NULL;

RCSP则持续追踪共有多少对象指向某笔资源,并在无人指向它时自动删除该资源。因此,trl::shared_ptrcopy行为更加直观一些。这两个智能指针都不能用于数组,因为,他们的析构函数调用的是delete而不是delete[](他们觉得,vectorstring已经足够了,没有必要再用数组)。可以看看boost的智能指针,那儿有对数组需求的支持

 

条款14: 在资源管理类中小心 copying 行为(Thinkcarefully about copying behavior in resource-managing classes)

本条款提醒程序员,使用资源管理类时需根据实际需要管理copying行为,常见的有:抑制copying(将copying操作声明为private);施行引用计数法(使用智能指针:trl::shared_ptr,当引用次数为0时,自动调用删除器);深度拷贝(复制指针和所指内存);转移底部资源的拥有权(将资源的拥有权从被复制物转移到目标物,例如auto_ptr)。

1、禁止复制(比如锁啊,数据库的链接啊,这种),实现的方式就是条款6讲的,将copying函数作为private或者继承Uncopyable类

2、底层采用 “引用计数”,及tr1::shared_ptr这种方法,就是说,资源记者又几个对象指着它,直到最后一个对象死了就释放资源

3、复制底层资源:深度赋值,不光复制指针,还复制指针指着的东西

4、转移资源的拥有权:就是一旦被copying,自己就失去了资源,例如auto_ptr这种。

 

条款15: 在资源管理类中提供对原始资源的访问(Provideaccess to raw resources in resource-managing classes)

1 APIs往往要求访问原始资源(raw resources) ,所以每一个RAII class 应该提供一个取得其所管理之资源的办法。

2)对原始资源的访问可能经由显式转换或隐式转换。一般而言显式转换(如调用get()成员函数)比较安全,但隐式转换对客户比较方便。

   tr1::shared_ptrauto_ptr都提供一个get成员函数,用来执行显示转换,也就是返回智能指针内部的原始指针(的复件)。就像所有智能指针一样, tr1::shared_ptrauto_ptr也重载了指针取值操作符(operator->operator*),它们允许隐式转换至底部原始指针。(即在对智能指针对象实施->*操作时,实际被转换为被封装的资源的指针。)

 

条款16: 成对使用new delete 时要采取相同形式(Usethe same form in corresponding uses of new and delete)

本条款提醒程序员在申请和释放资源时应采用相同形式:如果在new 表达式中使用[],必须在相应的delete表达式中也使用[];如果你在new 表达式中不使用[],一定不要在相应的delete表达式中使用[]

new被调用时,首先分配内存,再针对此内存会有一个(或更多)构造函数;当delete被调用时,针对此内存会有一个(或更多)析构函数被调用,再释放内存。由于delete必须知道即将被删除的内存内究竟存在多少个对象,从而决定调用多少个析构函数,所以采用相同形式很重要。

   最好尽量不要对数组形式作typedefs动作。因为这样容易引起delete操作的疑惑

 

条款17: 以独立语句将newed 对象置入智能指针(Storenewed objects in smart pointers in standalone statements)

本条款涉及C++对语句的执行次序,如果次序不能确定,最好写成独立的语句。以智能指针为例,给出的建议是:以独立语句将newed 对象存储于(置入)智能指针内。如果不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄漏。假设:

processWidget(std::trl::shared ptr<Widget> (new Widget) , priority());

在调用processWidget之前,编译器必须创建代码,做以下三件事:

1)执行“new Widget”

2)调用priority

3)调用trl::shared ptr构造函数

不同的C++ 编译器执行这三条语句的顺序不一样,但对priority的调用可以排在第一或第二或第三执行。如果编译器选择以第二顺位执行且priority函数抛出了异常,则新创建的对象Widget将导致内存泄漏。解决方法如下:

std::trl::shared_ptr<Widget> pw(new Widget);  //在独立语句内以智能指针存储Widget对象
processWidget(pw, priority());   //这个调用肯定不存在内存泄漏

 

这一章的核心思想:用类去管理资源,然后,我们只要按规矩(这个规矩已经系统化,成熟化)去写好我们的资源管理类(核心就是copying函数)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值