基于对象的资源管理方法,建立在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; //同上,无任何改变
假设有如下两个函数:
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)。
条款13中使用auto_ptr或tr1::shared_ptr保存factory函数如createInvestment的调用结果:std::tr1::shared_ptr<Investment> pInv( createInvestment() );
假设你想以函数处理Investment对象,int daysHeld(const Investment* pi);
有两个办法,显式转换和隐式转换。
n 显式转换
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);
n 隐式转换
class Font{
public:
…
operator FontHandle()const {return f;} //提供隐式转换函数
…
};
Font f(getFont());
int newFontSize;
changeFontSize(f, newFontSize);
如果你在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;
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());