Posted on 2008-08-12 10:37
chemz 阅读(3124)
评论(3) 编辑 收藏 引用 所属分类:
C++
CEnumClass—类型安全的枚举类型
在前面的一篇文章《enum类型的本质》中非常详尽的讨论了C++中原生枚举类型的一些
本质特征(http://www.cppblog.com/chemz/archive/2007/06/05/25578.html)。在文章的结
尾遗留了这样一个话题:
由上面的说明枚举类型有那么多的缺点,那我们怎样才能够有一个类型安全的枚举类型呢?
同时也给出了解决这个问题的一个思想:
其实可以采用类类型来模拟枚举类型的有限常量集合的概念,同时得到类型安全的好处。
沿着上面所阐述的思想来看为了解决这个问题必须通过C++中的类类型来强化类型的鉴别
能力,避免出现枚举类型和默认的int的隐示自动转换。我们来看看下面的实现:
template<typename SubT>
class CEnumClass
{
public:
friend bool operator==( const SubT &lhs, const SubT &rhs )
{
return lhs.value() == rhs.value();
}
friend bool operator!=( const SubT &lhs, const SubT &rhs )
{
return !( lhs == rhs );
}
int value() const { return m_value; }
protected:
CEnumClass( int i ) : m_value( i ) {}
CEnumClass( const CEnumClass &rhs ) : m_value( rhs.m_value ) {}
CEnumClass &operator=( const CEnumClass &rhs ) { m_value = rhs.m_value; return *this; }
protected:
int m_value;
};
首先简单的说明一下这个类,CEnumClass是一个奇异递归模板,也就是说模板参数SubT
是CEnumClass<SubT>的子类,那么这样一来为了声明一个枚举类就必须要想下面这样:
EType.hpp
class EType : public CEnumClass<EType>
{
};
上面声明就完成了enum EType这样一个过程,但是一个枚举类型不仅仅只是有类型,其中还
包括了有限常量枚举子,那我们就需要在类声明中添加对应的枚举子的定义:
EType.hpp
class EType : public CEnumClass<EType>
{
public:
static const EType e1;
static const EType e2;
static const EType e3;
private:
EType( int i );
};
================================================================================
EType.cpp
const EType EType::e1 = 0;
const EType EType::e2 = 1;
const EType EType::e3 = 2;
EType::EType( int i ) : CEnumClass<EType>( i )
{
}
有了上面的定义就完成了一个枚举类型的定义了,现在我们来分析一下这个类型是否真的能
够解决文章《enum类型的本质》中所提到的所有缺陷和陷阱,同时又不失为真正的枚举含义。
1. 有限常量集合
由于EType类的构造函数是私有的,所以无法在class声明之外定义任何类的实例,所以
所有的类实例必须在class声明中定义,有限性得到满足。常量属性可以通过是否能够修改
实例的内容来进行判断,这里没有任何办法可以修改枚举子e1、e2、e3中的内容,因为他
们本身是const属性,同时其成员均是外部不可访问的。
2. 尺寸大小
分析一下这个类就可以很清楚的判断出sizeof( EType ) == sizeof( int ),尺寸固定
成为一个int类型的大小,不再是可以随着其取值范围变化的了,同时也没有占用任何额外
的空间,空间利用率非常高效。
3. 边界约束
如果要定义一个枚举类型的变量,就必须像下面这样子:
EType val1 = EType::e1;
EType val2 = EType::e2;
EType val3 = EType::e3;
你不可能像下面这样子:
EType val1;
EType val2 = 1;
EType val2 = 100;
也就是说一个枚举变量的初始值必须在集合范围之内,不可能越过这个范围,也不可能取随
机值。
4. 代替int类型
文章《enum类型的本质》中也明确的讨论了enum类型不能在任何情况下替代int类型,而
在这里只要通过value函数调用就可以返回对应的int值,不就可以替代int类型了么。也就
是像这样:
int i = val1.value();
这种做法是显示的,你现在知道你自己在干什么,而不是编译器隐示为你完成的。
对于其他剩余的内容,比如比较、输入输出等大家可以自行练习完成,这里就不论述了。