Effective C++ 读书笔记(三)

3 资源管理

条款13:以对象管理资源

基于对象的资源管理方法,建立在C++对构造函数、析构函数、copying函数的基础之上,依赖c++的“析构函数自动调用机制”确保资源被释放。

 

auto_ptr和 shared_ptr一个简单的基本实现,只反应其基本原理。

template<typename _Tp>

class auto_ptr

{

private:

         _Tp* _M_ptr;

 

public:

typedef _Tp element_type;

explicit auto_ptr( element_type* __p =  0) :_M_ptr(__p) { }

 

template<typename _Tp1>

auto_ptr( auto_ptr <_Tp1> & __a) : _M_ptr ( __a.release() ) { }

 

         //注意,赋值的时候,同时会把原来的auto_ptr变量中的指针设置为NULL

         auto_ptr& operator=(auto_ptr& __a)

         {

                   reset(__a.release());

                   return *this;

         }

         template<typename _Tp1>

         auto_ptr& operator=(auto_ptr<_Tp1>& __a)

         {

                   reset(__a.release());

                   return *this;

         }

// 重载* 和-> 让auto_ptr看起来更像是个指针。

         element_type& operator*() const

         {

                   _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);

                   return *_M_ptr;

         }

         element_type*operator->() const

         {

                   _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);

                   return _M_ptr;

         }

         //注意他的实现

         element_type*release()

         {

                   element_type* __tmp = _M_ptr;

                   _M_ptr = 0;

                   return __tmp;

         }

         void reset(element_type* __p = 0)

         {

                   if (__p != _M_ptr)

                   {

                            delete _M_ptr;

                            _M_ptr = __p;

                   }

         }

};

         template<class T>

         class shared_ptr

         {

         public:

                   typedef unsigned long size_type;

         private:

                   void dispose()

                   {

                            if (--*pn == 0)

                            { delete px; delete pn; }

                   }

 

                   T* px; // contained pointer

                   size_type* pn; // reference counter

 

         public:

                   explicit shared_ptr(T* p=0): px(p)

                   {

                            pn = new size_type(1)                   

                   }

 

                   shared_ptr& operator= (T* p)

                   {

                            if(this->px == p) return *this;

 

                            dispose();

                            pn = new size_type(1);

                            px=p;

                            return *this;

                   }

 

shared_ptr(const shared_ptr& r) throw(): px(r.px)

                   {

                            ++*r.pn;

                            pn = r.pn;

                   }

 

shared_ptr& operator= (const shared_ptr& r) throw()

                   {

                            if(this == &r) return *this;

                            dispose();

                            px = r.px;

                            ++*r.pn;

                            pn = r.pn;

                            return *this;

                   }

         }

std::auto_ptr<Investment>  pInv1(createInvestment());

std::auto_ptr<Investment>  pInv2(pInv1); //Now, pInv2指向对象,pInv1设为null

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

 

std::tr1::shared_ptr<Investment>  pInv1(createInvestment());

std::tr1::shared_ptr<Investment>  pInv2(pInv1); //Now, pInv2,  pInv1指向同一个对象

pInv1 = pInv2; //同上,无任何改变

 

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

假设有如下两个函数:

void lock( Mutex* pm); //锁定pm所指的互斥器

void unlock(Mutex* pm);//将互斥器解除锁定

建立一个class来管理锁:

class Lock{

public:

         explicit Lock(Mutex*  pm): mutexPtr(pm) { lock(mutexPtr); }

         ~Lock() { unlock(mutexPtr); }

private:

         Mutex* mutexPtr;

};

应用:

Mutex m;

 

{

Lock m1(&m)

}//自动解锁

但如果Lock对象被复制,会发生什么??

Lock m1(&m);

Lock m2(m1);

解决方案有:1, 禁止复制。 2,对底层资源祭出引用计数法,保有资源只到它的最后一个使用被销毁。

 

如:

class Lock{

public:

         explicit Lock(Mutex*  pm): mutexPtr(pm,  unlock) { lock(mutexPtr.get()); }

         //以某个Mutex初始化shared_ptr,并以unlock函数为删除器

private:

         std::tr1::shared_ptr<Mutex>  mutexPtr;

};

class的析构函数会自动调用non-static成员变量的析构函数。mutexPtr的析构函数会在互斥器的引用计数为0时调用删除器(本例为unlock)。

 

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

条款13中使用auto_ptr或tr1::shared_ptr保存factory函数如createInvestment的调用结果:std::tr1::shared_ptr<Investment>  pInv( createInvestment() );

假设你想以函数处理Investment对象,int daysHeld(const Investment* pi);

有两个办法,显式转换和隐式转换。

显式转换

shared_ptr和auto_ptr提供get函数,返回智能指针内部的原始指针。

即int days = daysHeld(pInv.get());

类似地:

FontHandle     getFont();

void  releaseFont(FontHandle fh);

class Font{

public:

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

         ~Font(){ releaseFont(f); }

         FontHandle get()const {return f;} //提供显式转换函数

private:

         FontHandle f;

};

void changeFontSize(FontHandle f, int newSize);

Font f(getFont());

int newFontSize;

changeFontSize(f.get(), newFontSize);

 

隐式转换

class Font{

public:

         …

         operator FontHandle()const {return f;} //提供隐式转换函数

         …

};

Font f(getFont());

int newFontSize;

changeFontSize(f, newFontSize);

 

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

如果你在new表达式中使用[ ],必须在相应的delete表达式中也使用[ ]。如果你在new表达式中没有使用[ ],一定不要在相应的delete表达式中使用[ ]。

如果你使用delete时加上[ ],delete便认定指针指向一个数组,否则它认定指针指向一个单一对象。

std::string *str1 = new std::string;

std::string *str2 = new std::string[10];

delete str1;

delete [ ]str2;

最容易犯错的情形是使用typedef的情形:

typedef std::string AddressLines[4];

std::string* pal = new AddressLines; //”new AddressLines”返回一个string*

delete [ ] pal;

 

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

int priority();

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

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

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

n  调用priority();

n  执行new Widget

n  调用shared_ptr的构造函数

执行顺序可能为:

1,  new widget

2, 调用priority

3, 调用shared_ptr构造函数

但若在第2步出现异常,将出现内存泄漏。

解决方案:

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

processWidget(pw, priority());

 

posted on 2012-11-21 22:39  ArcherXu  阅读(225)  评论(0编辑  收藏  举报

导航