Conclusion for Resource Management

来源:互联网 发布:淘宝能在1688 编辑:程序博客网 时间:2024/05/22 23:31

条款13:

1.再看工厂函数

我们使用一个投资行为库,其中各式各样的投资行为继承自一个root class Investment:

class Investment{};
进一步假设这个程序库通过工厂函数(条款7)供应我们特定的Investment对象:

Investment* createInvestment();  //返回指针,指向Investment继承体系的动态分配对象。调用者有责任删除它。
void fun()  {      Investment* pInv=CreateInvestment();      ……      delete pInv;//释放资源  }  
看起来正常,但是若在delete语句之前程序结束(比如return或异常)。
2.我们需要将资源放进对象内,当控制流离开f,该对象的析构函数会自动释放 哪些资源。

auto_ptr是一个类,用来实现对动态分配对象的自动释放。auto_ptr在构造时获取对某个对象的所有权,在析构时释放该对象。

void fun(){std::auto_ptr<Investment> pInv = CreateInvestment();}
这样离开函数时,资源被自动回收。
3.auto_ptr有一个不寻常的性质:若通过copy构造函数或copy assignment操作符复制它们,它们会变成null,而复制所得的指针将取得资源的唯一拥有权。如果让多个auto_ptr指向同一对象,则对象会被删除一次以上。
void f(){    std::auto_ptr<Investment> pInv(createInvestment());    std::auto_ptr<Investment> pInv2(pInv);     //pInv 变为Null pInv指向对象    pInv = pInv2                               //pInv 指向对象 pInv2变为NULL}

auto_ptr不适用有复制的容器身上。

4.shared_ptr作用如同指针,但会记录有多个shared_ptr共同指向同一个对象,这便是引用计数,计数为0时,会被自动删除。
void f(){    std::tr1::shared_ptr<Investment> pInv(createInvestment());    std::tr1::shared_ptr<Investment> PInv2(pInv);    //PInv和PInv2同时指向一个对象。    PInv = Pinv2;}
5.auto_ptr和tr1::shared_ptr都在其析构函数内做delete而不是delete []动作。因此不要在动态分配而得的array身上使用auto_ptr和shared_ptr。

虽然下面也可以通过编译,但是不要这样用:

std::auto_ptr<std::string> aps(new std::string[10]);
可以用vector和string取代动态分配而得的数组。



条款14:

1.为了确保不会忘记将一个被锁住的Mutex解锁,可以建立一个class用来管理锁。

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; };
Lock的使用如下:

Mutex m;                        //定义需要的互斥器//...{Lock m1(&m);  //锁住互斥器//...}                      //在区域最末尾,自动解除互斥器锁定


2.当一个RAII(在资源取得时机辨识初始化时机)对象被复制,会发生什么事?

第一种可能:禁止复制

将sopying操作声明为private。

Class Lock:private Uncopyable  //禁止复制(条款6){  //……};
第二种可能:对底层祭出“引用计数法”,也就是用shared_ptr实现底层。

calss Lock{public:explicit Lock(Mutex* pm):mutexPtr(pm,unlock){        lock(mutexPtr.get());}private:std::tr1::shared_ptr<Mutex> mutexPtr;};
注:不用声明析构函数。条款5说个,class析构函数(无论编译器生成还是用户定义)会自动调用其non-static成员变量的析构函数。




条款15:

1.将RAII class对象转换为其所含的原始资源。

shared_prt<Investment> pInv=(createInvestment());  
但是处理Investment对象的函数像这样:

int daysHeld(const Investment* pi);  //返回投资天数
下面的调用不能通过编译,因为daysHeld需要的是Investment*指针,传给他的却是个类型为tr1::shared_ptr<Investment>的对象。

int days=daysHeld(pInv);
shared_ptr和auto_ptr都提供一个get成员函数,用来执行显示转换,也就是他会返回智能指针内部的原始指针(的复件)

int days=dayHeld(pInv.get());  
2.就像所有智能指针一样,tr1::shared_ptr和auto_ptr也重载了指针取值操作符(operator->和operator*),它们允许隐式转换至底部原始指针:

class Investment{public:bool isTaxFree() const;//……};Investment* createInvestment();            //factory函数std::tr1::shared_prt<Investment> pi(createInvestment());pi->isTaxFree();//通过operator->访问  (*pi).isTaxFree();//通过operator*访问  
3.有时候我们必须使用RAII class内的原始资源。做法是提供隐式转换函数。

FontHandle getFont();//C的API。为求简化省略参数  void releaseFont(FontHandle fh);    class Font{//RAII class  public:      explicit Font(FontHandle fh):f(fh)      {}      ~Font(){releaseFont(f);}  private:      FontHandle f;  };  
假设有大量与字体相关的C API,它们处理的是FontHandles,那么“将Font对象转换为FontHandle”会是一种很烦繁的需求。Font class可为此提供一个显示转换函数。

class Font{  public:      FontHandle get()const{return f;}      //……  };  
不过客户要想使用API就必须调用get。另一种做法是为Font提供隐式转换函数,转型为FontHandle:

class Font{  public:      operator FontHandle ()const{return f;}//隐式转换      ……  }; 
注:C++可通过operator 隐式转换,格式如下: operator 类型T (),T是要转换到的类型。上面可以使Font隐式转换为FontHandle。




条款16:
1.new 对应delete;new type[]对应delete []。




条款17:
1.C++完成参数的核算顺序不定。
假设两个函数,一个解释优先权,另一个进行带优先权的处理:

int priority();void processWidget(tr1::shared_ptr<Widget> pw, int priority);
现在考虑processWidget:

processWidget(new Widget, priority());
是的,无法通过编译,构造函数是explicit函数,无法将原始指针隐式转为shared_ptr。下面这样可以编译:
processWidget(tr1::shared_ptr<Widget>(new Widget), priority());
可惜这样也可能泄露资源,原因是C++完成参数的核算顺序不定,可能的顺序如下:
执行“new Widget”

调用priority

调用tr1::shared_ptr

如果priority调用异常,“new Widget”返回的指针将会遗失,造成泄漏。
2.避免1中问题很简单,使用分离语句。

tr1::shared_ptr<Widget> pw(new Widget);//在单独语句内以智能指针存储newed所得对象。 processWidget(pw, priority());
以上行得通,是因为编译器对于“跨越语句的各项操作”没有重新排列自由。


0 0
原创粉丝点击