C++学习:一个简单适用的跨平台Log类(1)

本文开发一个简单适用的日志类Log,讲述C++一些比较好玩的特性,template、策略类、多线程锁、单件、函数可变参数等的方法。涉及的东西比较多,可能一篇写不完。


在开始Log类之前,先讲解几个简单的class,这些class往往比较小,完成单一的功能,利用C++多重继承机制,把这些小类拼装成比较复杂的类。这些小class,称为策略类。枯燥无味的概念不是重点,《C++设计新思维》的第一章有详细讲述。我只show代码,用代码说明。


第一个类是NoCopy,字面意思就是让类不能复制,具体的就是不能通过构造函数和赋值函数来构造对象。要达到这个目的,只需要自定义类型从这个类派生即可。原理就是把拷贝构造函数和赋值函数屏蔽掉,叫编译器编译不过。代码如下:

class NoCopy
{
        NoCopy( const NoCopy& );//私有
	NoCopy& operator=( const NoCopy & ); //私有
public:
	NoCopy(void){}//提供缺省的构造函数
};

这里明确定义缺省构造函数,原因是声明了私有的拷贝构造函数和赋值函数,结果缺省构造函数就没了,派生类会出现编译错误,报告NoCopy无缺省构造函数。


第二类是一组类,跟多线程有关。多线程环境下,考虑到并发情况,数据的访问要保证互斥,不能破坏掉。一些伪码如下:

class Something
{
public:
    bool WriteOp(...)
    {
        m_lock.Lock();
        ...
        m_lock.Unlock();
    }
private:
    XxxLock m_lock;//互斥对象
};
为了实现跨平台,所使用的互斥对象,不能依赖具体某个平台,比如win32,而且要求这种加锁机制,由使用者自己决定是否采用。互斥的时候,需要成对使用Lock和Unlock,防止忘记解锁,导致死锁。一个惯用法比较常用,就是利用构造函数和析构函数。首先来看门卫锁,也叫范围锁的类型:

template<typename LockT>
class GuardLock
{
public:
	GuardLock( const LockT &lock)
		:m_lock( const_cast<LockT&>(lock) )
	{
		m_lock.Lock();
	}

	~GuardLock(void)
	{
		m_lock.Unlock();
	}

private:
	LockT &m_lock;
};
GuardLock类型并未对所使用的互斥对象进行限制,通过模板参数来指定。这样可以方便实现跨平台。使用方法如下:

CCriticalSection m_cs;//win32的关键代码段

GuardLock<CriticalSection> lock;
关于锁的类型,在《C++设计新思维》一书中说了主要有三种:

1、空锁,也就是单线程,Lock和Unlock是空操作;

2、对象锁,就是每个对象都有一个互斥对象,加锁针对单个对象;

3、类锁,就是每个类有一个互斥对象,加锁针对类;

三个不同的锁类型,我们可以通过三个小的class来实现,类型只要从这些小class派生,就可以具备不同的加锁保护。首先来看一下空锁的类型定义:

template<class Host>
class EmptyLock
{
public:
	struct Lock
	{
		Lock() {}
		explicit Lock( const EmptyLock& ) {}//子类是父类的关系是:is a
	};
};
上述的模板Host,是指所要定义的派生类。具体的用法,后面再介绍。还有两个类型的锁,定义为:

template<typename Host, typename LockT>
class ObjectLock
{
	LockT m_lock;//普通成员
public:
	ObjectLock()
		:m_lock()
	{
	}
	~ObjectLock()
	{
	}

	class Lock;
	friend class Lock;
	class Lock : NoCopy//嵌套类
	{
		ObjectLock& m_host;
	public:
		explicit Lock(const ObjectLock& host)
			: m_host(const_cast<ObjectLock>(host))
		{
			m_host.Lock();
		}
		~Lock(void)
		{
			m_host.Unlock();
		}
	};
};

template <typename Host, typename LockT>
class ClassLock
{
	static LockT s_classLock;//静态成员
public:
	class Lock;
	friend class Lock;

	class Lock : NoCopy
	{
	public:
		explicit Lock( const ClassLock& )
		{
			ClassLock::s_classLock.Lock();
		}
		~Lock(void)
		{
			ClassLock::s_classLock.Unlock();
		}
	};
};

template <typename Host, typename LockT>
LockT ClassLock<Host, LockT>::s_classLock;//定义静态成员变量

对象锁和类锁的不同地方,就是互斥对象,一个是普通成员变量,一个是静态成员变量,大家都知道,静态成员变量是属于类的,不属于对象。关于对象和类的区别,没有搞明白的,可能需要加深理解。简单的举一个例子,比如狗,狗是类,一只叫小白的狗则是一个对象。现在设计一个类,具备对象锁的互斥能力。代码如下:

class MyClass : public ObjectLock<MyClass, CCriticalSection>
{
public:
    void TestLock(void)
    {
        Lock lock(*this);//这里就是调用具体的加锁函数
        
        //other code;
    }
};
这里使用win32的关键代码段举例子,使得MyClass就不是模板类了。现在MyClass类就具备了对象锁的特性。如果后续需要改成类锁,就只需要改一下类型的声明即可:

class MyClass : public ClassLock<MyClass, CCriticalSection>
{
};

现在,也许大家已经初步见到这些小class(策略类)的作用,通过简单的组建,就可以使得类型具备一些复杂的特性,而改动地方很小。对于一个严肃的开发人员来说,写超过100行的函数,一般会比较小心谨慎的。同样,对于一个类型来说,同样是如此,把一个类型设计成小儿简单的,也是代码设计的基本功,C++远远不是带class的C。


说了这么多,还只是Log类一些准备工作,下一篇再来讲述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值