Effective C++读书笔记(五)资源管理部分

读书笔记中涉及到的所有代码实例可通过https://github.com/LuanZheng/EffectiveCPlusPlus.git进行下载获得。

Item13: 以对象管理资源

先看一下下面的例子:

void f()
{
    Investment* pInv = createInvestment();
    ...
    delete pInv;
}

如果…语句里面产生异常,或者有过早的return语句等,那么,delete语句将得不到执行的机会,因此,会造成内存的泄露。
为确保createInvestment返回的资源总是被释放,我们需要将资源放进对象内,当控制流离开f,该对象的析构函数会自动释放哪些资源。

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

获得资源后立刻放进管理对象内。
管理对象运用析构函数确保资源被释放。
由于auto_ptr被销毁时会自动删除它所指之物,所以一定要注意别让多个auto_ptr指向同一对象。
auto_ptr有一个不寻常的性质:若通过copy构造函数或copy assignment操作符复制它们,它们会变成null.而复制所得的指针将取得资源的唯一使用权!

#include <memory>
#include <iostream>
#include "SimpleFactory.h"
int main()
{
       if (true)
       {
              //Investment *pInv = SimpleFactory::getInstance()->createInv();  //这种做法将导致可能的内存泄露
              std::auto_ptr<Investment> pInv(SimpleFactory::getInstance()->createInv());  //以对象管理资源(智能指针),在对象析构时,会自动释放资源
              std::auto_ptr<Investment> pInv1(SimpleFactory::getInstance()->createInv());  //以对象管理资源(智能指针),在对象析构时,会自动释放资源
              std::auto_ptr<Investment> pInv2(pInv1);  //以对象管理资源(智能指针),在对象析构时,会自动释放资源
              pInv1 = pInv2;
              std::cout << "Auto Ptr will be finished." << std::endl;
       }
       else
       {
       }
       std::cout << "Finished." << std::endl;
       return 0;
}

auto_ptr的替代方案是“引用计数型智慧指针(RCSP)”。所谓RCSP也是个指针类型,持续追踪共有多少个对象指向某笔资源,并在无人指向它时自动删除该资源。

#include <memory>
#include <iostream>
#include "SimpleFactory.h"
void AutoPtrVersion()
{
       //Investment *pInv = SimpleFactory::getInstance()->createInv();  //这种做法将导致可能的内存泄露
       std::auto_ptr<Investment> pInv(SimpleFactory::getInstance()->createInv());  //以对象管理资源(智能指针),在对象析构时,会自动释放资源
       std::auto_ptr<Investment> pInv1(SimpleFactory::getInstance()->createInv());  //以对象管理资源(智能指针),在对象析构时,会自动释放资源
       std::auto_ptr<Investment> pInv2(pInv1);  //拷贝构造函数,会导致pInv1变成null,pInv2变成所指的对象
       pInv1 = pInv2;                           //赋值运算符,会导致pInv2变成null,pInv1变成所指的对象
       std::cout << "Auto Ptr will be finished." << std::endl;
}
void RCSPVersion()
{
       std::tr1::shared_ptr<Investment> pInv1(SimpleFactory::getInstance()->createInv());
       std::tr1::shared_ptr<Investment> pInv2(SimpleFactory::getInstance()->createInv());
       pInv1 = pInv2;              //不会有auto_ptr的奇怪行为,pInv1,pInv2均指向同一内存(pInv2),pInv1的原来内存会被释放
       std::cout << "RCSP will be finished." << std::endl;
}
int main()
{
       if (true)
       {
              AutoPtrVersion();
              RCSPVersion();
       }
       else
       {
       }
       std::cout << "Finished." << std::endl;
       return 0;
}

auto_ptr和tr1::shared_ptr两着都在析构函数中做delete,而不是delete[].因此,在动态分配的array身上使用auto_ptr或tr1::shared_ptr是个馊主意。这类动态分配的数组,可以利用vector和string来取代。

例子见Item13

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

资源管理类:构造时获得资源,析构时释放资源。

class Lock
{
    public:
        explicit Lock(Mutex* pm) : mutexPtr(pm)
        {lock(mutexPtr)};
        ~Lock() {unlock(mutexPtr);}
    private:
        Mutex *mutexPtr;
}

资源管理类与智能指针的区别是,智能指针需要在heap上分配,然而并非所有的资源都是分配在heap上的,对于分配在stack上的资源,就适用与资源管理类了。

当一个RAII对象被复制,大多数时候你会选择以下两种可能:

禁止复制
包括将拷贝构造函数和拷贝复制运算符定义为private,继承Uncopyable基类等等。

对底层资源祭出“引用计数法”
有时候我们希望保有资源,知道它的最后一个使用者被销毁。这种情况下复制RAII对象时,应该将资源的“被引用数”递增。

例子见Item13

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

在现实情况中,许多API直接指涉资源,因此,常常不得不绕过资源管理对象直接访问原始资源。

如下面daysHeld方法

因此需要提供显示转换来获取原始指针。

Investment* Investment::get()
{
       return this;
}

另外,tr1::shared_ptr和auto_ptr也重载了指针取值操作符(->和*),他们允许隐式转换至底部原始指针。在使用中,可以像原始指针一样(但类型不是原始指针类型)。

例子见Item13

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

new <-> delete
new[] <-> delete []
要成对使用。

当撰写的构造函数中进行内存动态分配时,要特别注意各个重载的构造函数使用同样的new/new[],否则析构函数将无法决定使用那个delete/delete[].

typedef std::string AddressLines[4];
std::string* pa1 = new AddressLines; //实际上是new string[4]
则在销毁内存时,需要: delete [] pa1; //正确
而非 : delete pa1; //错误

为了避免错误,最好尽量不要对数组形式做typedef动作。取而代之,尽量使用string, vector等。

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

研究以下代码:

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

该行代码将做三件事情:

执行priority()
执行 new Widget
执行tr1::shared_ptr构造函数

这三步的执行顺序无法确定,可以确定的是,new Widget一定在tr1::shared_ptr的构造函数之前执行。

若按照如下的执行顺序,

执行 new Widget
执行priority()
执行tr1::shared_ptr构造函数

一旦在priority()执行过程中抛出异常,由于此时Widget尚未放置入对象中,则会导致内存泄露。

因此,我们应该做如下改进:

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

则可保证执行顺序,保证以对象管理资源,从而避免异常导致的内存泄露。

例子见Item13

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值