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

来源:互联网 发布:怎样打开Windows目录 编辑:程序博客网 时间:2024/06/05 17:14
本文开发一个简单适用的日志类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类一些准备工作,下一篇再来讲述。

原创粉丝点击