Effective C++之三:以对象管理资源

来源:互联网 发布:linux scp 下载文件夹 编辑:程序博客网 时间:2024/05/21 21:50

条款13 以对象管理资源

(1)

为防止资源泄露,请使用RAII(资源获取时机便是初始化时机)对象,它们在构造函数中获得资源并在析构函数中释放资源。

“以对象管理资源”的观念常被称为“资源取得时机便是初始化时机”(RAII),因为我们总是在获得资源后于同一句话以它初始化(或赋值)某个管理对象。

void f()

{

    Investment* pInv = createInvestment();  //返回动态分配的对象。调用者有责任删除它。

    ...

    delete pInv;   //释放所指的对象

}


管理对象在离开区块会被销毁,自然会调用管理对象的析构函数,一时资源被释放。

void f()//“以对象管理资源的观念常被称为资源取得时机便是初始化时机RAII),因为我们总是在获得资源后于同一句话以它初始化某个管理对象。

{

    auto_ptr<Investment> pInv(createInvestment());  //createInvestment()返回的资源被当做其管理者auto_ptr的初值

    ...


}


(2)多个auto_ptr不能同时指向同一对象。

    auto_ptr<Investment> pInv1(createInvestment());

    auto_ptr<Investment> pInv2(pInv1);      //现在pInv2指向对象,pInv1设为null

    pInv1 = pInv2;                          //现在pInv1指向对象,pInv2设为null


(3)多个shared_ptr可以同时指向同一对象。


void f()

{

    shared_ptr<Investment> pInv(createInvestment());

    shared_ptr<Investment> pInv2(pInv1);        //现在pInv2pInv1只向同一对象

    pInv1 = pInv2;                              //同上

    ...

}//pInv2pInv1同时被销毁,所指对象也被自动销毁


(4)不能在动态分配的数组上使用auto_ptr和shared_ptr。

因为vector和string几乎总是可以取代动态分配而得的数组。

Boost::scoped_array 和 boost::shared_array classes也可以满足。


(5)记住!!!只能括号调用构造函数, = 调用会失败

    shared_ptr<int> sp1 =new int(2);  //编译过不了

    shared_ptr<int> sp2(newint(3));   //成功


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

复制RAII对象必须一并复制它所管理的资源,所以资源的copying行为决定RAII对象的copying行为。

void lock(Mutex* pm);

void unlock(Mutex* pm);


class Lock{

public:

    explicit Lock(Mutex* pm):mutexPtr(pm)

    {

        lock(mutexPtr);

    }

    ~Lock()

    {

        unlock(mutexPtr);

    }

private:

    Mutex* mutexPtr;

};


int main()

{

    Metux m;

    ...

    {   //某个区块里

        Lock ml1(&m);   //锁定互斥锁

        Lock ml2(ml1);  //ml1复制到ml2身上,会发生什么事?

    }

    return0;

}

下面是解决办法:普遍而常见的RAII class copying行为是: 抑制copying施行引用计数法(reference counting)。不过其他行为也都可能被实现。


(1)禁止复制

使用条款6的办法:将copying构造函数设为private。


(2)对底层的资源使用“引用计数法”。

注意:shared_ptr的缺省行为是“引用次数为0时便删除其所指物”。但是shared_ptr允许指定“删除器”--一个函数或者函数对象,当引用次数为0时便调用。

void lock(Mutex* pm);

void unlock(Mutex* pm);

class Lock{

public:

    explicit Lock(Mutex* pm): mutexPtr(pm, unlock) //以某个Mutex初始化shared_ptr,并以unlock函数为删除器

    {

        lock(mutexPtr.get());   //条款15get(),返回智能指针内部的原始指针

    }

    //注意:当前不再需要声明析构函数。因为类的析构函数(无论编译器生成还是自己写的)会自动调用其non-static成员变量(mutexPtr)的析构函数

    //而mutexPtr的析构函数会在互斥器的引用次数为0时自动调用shared_ptr的删除器(本例是unlock)

private:

    shared_ptr<Mutex> mutexPtr;    //使用shared_ptr替换原始指针

};


(3)复制底部资源。针对一份资源拥有其任意数量的复件。

在复制资源管理对象,也可以复制其所包含的资源。也就是说进行的是深入拷贝。


(4)转移底部资源的拥有权:auto_ptr


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

APIs(比如说智能指针)往往要求访问原始资源(raw resources,也就是那个原始的指针), 所以每一个RAII class应该提供一个“取得其所管理之资源”的方法。

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

(1)shared_ptr提供get成员函数,执行显式转换,返回其所包含之原始资源。

int daysHeld(const Investment* pi);


shared_ptr<Investment> PInv(creatInvestment());

int days = daysHeld(PInv); //错误!

int days = daysHeld(PInv.get());//shared_ptr提供get成员函数,执行显式转换,返回其所包含之原始资源。


(2)shared_ptr也重载操作符 "->" "*",允许隐式转换至底部原始指针。

class Investment{

public:

    bool isTaxFree()const;

    ...

};


shared_ptr<Investment> pi1(creatInvestment());

bool taxable1 = !(pi1->isTaxFree());


auto_ptr<Investment> pi2(creatInvestment());

bool taxable2 = !((*pi2).isTaxFree());


(3)对于返回类对象里面的一个原始资源,有两种方式。

void releaseFont(FontHandle fh);


class Font{

public:

    explicit Font(FontHandle fh):f(fh){}

    ~Font(){releaseFont(f);}

    

    FontHandle get() const //方式一:显式转换函数。最主要的目的是防止资源(FontHandle)泄露

    {return f;}

    

    operator()const //方式二:另一个方法是Font提供一个隐式转换函数

    {return f;}

    

private:

    FontHandle f;

};


条款16 成对使用new和delete时要采取相同形式

(1)

new 有两件事:

1、内存被分配

2、一或多个构造函数被调用


delete 两件事:

1、一或多个析构函数被调用

2、内存被释放

(2)

如果你在new表达式中使用[],必须在delete表达式中也使用[]。

如果new不使用[],delete也一定不使用!

(3)

typedef string AddressLines[4];//尽量不要对数组形式做typedef。改用vectortypedef用法还需要熟悉。 特别是这种[4]写在后面的形式。


int main()

{

    string* pal =new AddressLines; //new AddressLines 相当于 new string[4]

    

    delete pal;    //错误

    delete[] pal;  //正确

    return0;

}


条款17 以独立语句将new对象置入智能指针

(1)

以独立语句将new对象置入智能指针。如果不这样做,其他部分可能会有异常抛出,在资源被创建与资源被转化为资源管理对象之间可能发生异常干扰,可能导致资源泄露。

int priority();

void processWidget(shared_prt<Widget> pw,int priority);


processWidget(shared_prt<Widget> (new Widget), priority());

//上面这个句话分为三部:

//1、调用priority()

//2、执行 new Widget

//3、执行shared_ptr构造函数

//但是编译器完成这三步的顺序可能会改变,改为 2 1 3。如果在priority()出错,那new出来的指针就回遗失,引发资源泄露。


//所以应该改为

shared_prt<Widget> pw(new Widget); //独立语句将newd对象存储于智能指针内

processWidget(pw, priority());




阅读全文
0 0
原创粉丝点击