C++语言中,对于一个枚举类型(enum),其成员值在所属枚举类型的声明作用域内是不可重复的。这个继承自C语言的特性,使我在写程序时碰到有相同名称的枚举成员时,苦于寻找且方便易读的替代枚举名称。
这在C++程序开发方面带来了许多的不方便,在涉及枚举类型时必须时刻关注与现有的枚举变量是否有重名,在一定程度上限制了命名的自主性,也会降低程序的可读性。而在Visual Basic 6和.net系列语言中,两个不同的Enum类型可以具有相同的成员,在使用时只需加上Enum类型名称。目前就是要在C++中实现这种功能。
一个例子,用以描述一台计算机和一个音箱的类,它们都有一个状态属性State。音箱状态值只有开和关两种,而计算机状态还有休眠状态。由于休眠对于音箱没有任何意义,在严格要求的编码下,对于向音箱赋上休眠状态的无意义举动必须在编译时阻止,因此需在使此两种设备类的状态属性分别两个状态枚举类型,计算机状态和音箱状态,代码如下:
上述代码在编译时就不能通过,因为两个枚举的成员都是全局的,stateOpen和stateClosed都重复出现。因此,现在需要做的就是将这两个状态枚举类型分在两个作用域中声明,可采用的方式是在不同的namespace中声明或在不同的类中声明。
{
stateOpen,
stateClosed,
stateSuspended
} ;
enum SpeakerState
{
stateOpen,
stateClosed
} ;
class Computer
{
public:
void SetState(ComputerState s) { ... }
} ;
在不同名称空间中声明枚举的代码如下:
enum ComputerState
{
stateOpen,
stateClosed,
stateSuspended
};
} ;
namespace speaker {
enum SpeakerState
{
stateOpen,
stateClosed
};
} ;
using namespace computer;
using namespace speaker;
class Computer
{
public:
void SetState(ComputerState s)
{
if ( s == computer::stateOpen ) printf("State set to Open ");
else if ( s == computer::stateClosed ) printf("State set to Closed ");
else if ( s == computer::stateSuspended )
printf("State set to Suspended ");
}
} ;
虽然代码编译运行都通过,但在不同的名称空间中声明显然不是个好办法,一是Computer::SetState()方法中状态判断是,状态值(stateOpen等)前面并不是类型名称而是namespace名称。这样在代码可读性方面还是比较差。第二,就是有多少个enum类型,就得用多少个namespace以及using namespace,这在实际使用中是无法忍受的。因此, 目前只能是在class中声明enum类,将enum类型class化。代码修改如下:
public:
enum _ComputerState {
stateOpen,
stateClosed,
stateSuspended
};
} ;
class SpeakerState {
public:
enum _SpeakerState {
stateOpen,
stateClosed
};
} ;
class Computer
{
public:
void SetState(ComputerState::_ComputerState s)
{
if ( s == ComputerState::stateOpen )
printf("State set to Open ");
else if ( s == ComputerState::stateClosed )
printf("State set to Closed ");
else if ( s == ComputerState::stateSuspended )
printf("State set to Suspended ");
}
} ;
上述的每个Enum的作用域限制在一个类中,这样实现了不同enum类型其成员的重,但在使用时的类型表述上还没有统一。因此,根据枚举类型变量常用的几个操作符,对其做重载。代码如下:
class ComputerState {
public :
enum _ComputerState {
stateOpen,
stateClosed,
stateSuspended
};
private :
_ComputerState m_val;
public :
ComputerState(): m_val(stateOpen) {};
ComputerState ( const _ComputerState & s): m_val(s) { };
bool operator == ( const ComputerState & cs ) {
if ( m_val == cs.m_val ) return true ;
else return false ;
}
operator _ComputerState() { return m_val; }
};
class SpeakerState {
public :
enum _SpeakerState {
stateOpen,
stateClosed
};
private :
_SpeakerState m_val;
public :
SpeakerState(): m_val(stateOpen) {};
SpeakerState ( const _SpeakerState & s): m_val(s) { };
bool operator == ( const SpeakerState & cs ) {
if ( m_val == cs.m_val ) return true ;
else return false ;
}
operator _ComputerState() { return m_val; }
};
上述代码增加了类型值成员变量m_val、类复制构造函数,重载了枚举类的等于比较操作符和类型解包操作符,从而实现了一个枚举类型的类化,实现了枚举成员与枚举类自动组包和解包,在使用上,枚举的使用和VB里面除了"."与"::"在操作符上语言本身的区别外,基本相同。最重要的是在枚举成员在命名时不必因为有重名而使用可读性差的或者增加不必要的区别符号或名称。上述枚举类的使用时代码示例如下
{
public :
void SetState(ComputerState s)
{
if ( s == ComputerState::stateOpen ) printf( " State set to Open " );
else if ( s == ComputerState::stateClosed ) printf( " State set to Closed " );
else if ( s == ComputerState::stateSuspended )
printf( " State set to Suspended " );
}
};
class Speaker
{
public :
void SetState(SpeakerState s)
{
if ( s == SpeakerState::stateOpen ) printf( " State set to Open " );
else if ( s == SpeakerState::stateClosed ) printf( " State set to Closed " );
}
};
int main( int argc, char * argv[])
{
Computer cpt;
Speaker spk;
ComputerState cs = ComputerState::stateClosed;
SpeakerState ss = SpeakerState::stateOpen;
cpt.SetState( ComputerState::stateOpen );
spk.SetState( SpeakerState::stateOpen );
cpt.SetState( cs );
spk.SetState( ss );
return EXIT_SUCCESS;
}
但是,由于enum的封装时增加了额外的构造函数和重载操作符,这部分代码将增加不少的工作量。为了减少这部分工作量,可以将这部分增加的函数定义成宏,如下:
#define ENUM_CLASS_DECLARE(cls_type, enum_type) /
public: enum_type m_val; /
public: /
cls_type ( const enum_type& s): m_val(s) {} ; /
bool operator == ( const enum_type& cs ) { /
if ( m_val == m_val ) return true; /
else return false; } /
operator enum_type() { return m_val; };
这样,上文的两个状态枚举类可以简化成下面的两个样子:
public :
enum _ComputerState {
stateOpen,
stateClosed,
stateSuspended
};
ENUM_CLASS_DECLARE(ComputerState, _ComputerState)
};
class SpeakerState {
public :
enum _SpeakerState {
stateOpen,
stateClosed
};
ENUM_CLASS_DECLARE(SpeakerState, _SpeakerState)
};
到此为止,目的只是为了解决由于C++中枚举类型的成员变量的全局唯一性引起的在成员命名时的苦恼,一个简单的OPEN状态可能会用在许多不同的设备或其他多种事物对象的状态枚举中,解决此问题,其一就是一个OPEN状态对于不同的设备使用不同的名称,比如ComputerOpen、SpeakerOpen,其二是创建一个硕大无比的Enum类型,包含软件中涉及到的所有设备所有事物对象的状态,其三就是本文所说的枚举类型的类封装。