天天看点

RAII的一些实现

总体来说,虽然是基本话题,不过还是可以写出一点点新意,也算是自己的代码总结。

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_;	
	};
           

这样使用起来就不必写出完整的类名了