总体来说,虽然是基本话题,不过还是可以写出一点点新意,也算是自己的代码总结。
RAII实现和智能指针一样,甚至可以理解为定制shared_ptr的Dx为资源释放方法即可。
不过对于个人来说,还是喜欢用意图明确,简单,专用化的代码。
个人工作中用过两种类型的,一种是Windows上各种HANDLE,HMODULE,Hxxx的释放,于是写了一些通用的代码:
template<typename T>
struct destory_t;
#define DESTORY_IMPL(type, method) \
template<>\
struct destory_t<type>\
{\
static void destory(type h)\
{\
method(h);\
}\
};
DESTORY_IMPL(HANDLE, CloseHandle)
DESTORY_IMPL(HMODULE, FreeLibrary)
template<typename T, template <typename U> class D = destory_t>
struct smart_handler
{
smart_handler(T handler)
: handler_(handler)
{}
~smart_handler()
{
D<T>::destory(handler_);
}
operator T() const
{
return handler_;
}
T handler_;
};
typedef smart_handler<HANDLE> SHandle;
typedef smart_handler<HMODULE> SModule;
这样就不担心忘记关闭啥的了,本质和smartptr一样,只不过更加专用。这里没用指针是因为windows下的各种句柄本来有指针语意,如果要照搬还得考虑下。
PS.网上很多人说这种代码是否有禁止copy是XX试金石,我的看法是,对于这样的类使用assign,copy ctor基本是故意找茬的代码,就如所谓string的assign检查自赋值一样(虽然还是得写,或用临时对象来实现)。
另外一种是XXGuard,这样的情况也比较多,比如各种加锁,解锁。
最基本情况,假设有两种锁:
struct critical_section_lock
{
void lock() { printf("critical_section_lock.lock()\n"); }
void unlock() { printf("critical_section_lock.unlock()\n"); }
};
struct spin_lock
{
void lock() { printf("spin_lock.lock()\n"); }
void unlock() { printf("spin_lock.unlock()\n"); }
};
其中,ctor,dtor分别是对内部数据结构的建立和删除,不予附上
最基本的写法是对两个lock分别写自己的guard。当然模版是更好的实现,如:
template<typename T>
struct lockguard
{
lockguard(T& lock)
: lock_(&lock)
{
lock_->lock();
}
~lockguard()
{
lock_->unlock();
}
T* lock_;
};
使用起来是这样的:
critical_section_lock g_cslock;
spin_lock g_splock;
void main()
{
lockguard<critical_section_lock> o(g_cslock);
}
其实也可以了,但一直觉得把完整模版参数类名写出来好麻烦,当然C++11的auto表示没压力。
设计思路:
1.因为lockguard是模版类,使用时必须指明类型名。所以首先必须要使得它不是模版类
2.构造函数能接受各种类型,所以构造函数得是模版函数
3.由于要保存实际的锁的指针(析构解锁用),而整个类不是模版类,所以这个指针类型得是普通指针
4.加解锁时为了能使用这个指针的lock函数编译过,所以这个指针得是接口类的指针
5.有以上思路后,最后需要的是用模版类实现这个接口类的方法
这样的思路其实也就是boost::any以及loki::function中的惯用手法。。。
最终代码:
struct ILock
{
virtual void lock() = 0;
virtual void unlock() = 0;
};
template<typename T>
struct TLock : public ILock
{
TLock(T* lock)
{
lock_ = lock;
}
void lock() { lock_->lock(); }
void unlock() { lock_->unlock(); }
T* lock_;
};
struct lockguard
{
template<typename T>
lockguard(T& lock)
: impl_(new TLock<T>(&lock))
{
impl_->lock();
}
~lockguard()
{
impl_->unlock();
}
auto_ptr<ILock> impl_;
};
这样使用起来就不必写出完整的类名了