现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

一个简单适用的跨平台Log类(1)

2014-03-17 06:12 工业·编程 ⁄ 共 3132字 ⁄ 字号 暂无评论

本文开发一个简单适用的日志类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类一些准备工作,下一篇再来讲述。

来源:KiteRunner

给我留言

留言无头像?