C++中enum枚举类型成员重名问题的解决方法 - Enum枚举类型的类封装


C++语言中,对于一个枚举类型(enum),其成员值在所属枚举类型的声明作用域内是不可重复的。这个继承自C语言的特性,使我在写程序时碰到有相同名称的枚举成员时,苦于寻找且方便易读的替代枚举名称。

这在C++程序开发方面带来了许多的不方便,在涉及枚举类型时必须时刻关注与现有的枚举变量是否有重名,在一定程度上限制了命名的自主性,也会降低程序的可读性。而在Visual Basic 6和.net系列语言中,两个不同的Enum类型可以具有相同的成员,在使用时只需加上Enum类型名称。目前就是要在C++中实现这种功能。

一个例子,用以描述一台计算机和一个音箱的类,它们都有一个状态属性State。音箱状态值只有开和关两种,而计算机状态还有休眠状态。由于休眠对于音箱没有任何意义,在严格要求的编码下,对于向音箱赋上休眠状态的无意义举动必须在编译时阻止,因此需在使此两种设备类的状态属性分别两个状态枚举类型,计算机状态和音箱状态,代码如下:

 上述代码在编译时就不能通过,因为两个枚举的成员都是全局的,stateOpen和stateClosed都重复出现。因此,现在需要做的就是将这两个状态枚举类型分在两个作用域中声明,可采用的方式是在不同的namespace中声明或在不同的类中声明。

enum  ComputerState
{
    stateOpen,
    stateClosed,
    stateSuspended
}
;

enum  SpeakerState
{
    stateOpen,
    stateClosed
}
;

class  Computer
{
public:
    
void SetState(ComputerState s) { ... }
}
;

 

在不同名称空间中声明枚举的代码如下:

 

namespace  computer  {
        
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化。代码修改如下:

class  ComputerState  {
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里面除了"."与"::"在操作符上语言本身的区别外,基本相同。最重要的是在枚举成员在命名时不必因为有重名而使用可读性差的或者增加不必要的区别符号或名称。上述枚举类的使用时代码示例如下

 

class  Computer
{
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; };

这样,上文的两个状态枚举类可以简化成下面的两个样子:

 

class  ComputerState {
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类型,包含软件中涉及到的所有设备所有事物对象的状态,其三就是本文所说的枚举类型的类封装。

你可能感兴趣的:(NS2)