天天看點

C++ 枚舉類型的思考

c++ 中的枚舉類型繼承于 c 語言。就像其他從 c 語言繼承過來的很多特性一樣,c++ 枚舉也有缺點,這其中最顯著的莫過于作用域問題——在枚舉類型中定義的常量,屬于定義枚舉的作用域,而不屬于這個枚舉類型。例如下面的示例:

enum fileaccess {

     read = 0x1,

     write = 0x2,

};

fileaccess access = ::read; // 正确

fileaccess access = fileaccess::read; // 錯誤

c++枚舉的這個特點對于習慣面向對象和作用域概念的人來說是不可接受的。首先,fileaccess::read 顯然更加符合程式員的直覺,因為上面的枚舉定義理應等價于如下的定義(實際上,.net 中的枚舉類型便是如此實作的):

class fileaccess {

     static const int read = 0x1;

     static const int write = 0x2;

其次,這導緻我們無法在同一個作用域中定義兩個同樣名稱的枚舉值。也就是說,以下的代碼是編譯錯誤:

enum fileshare {

     read = 0x1, // 重定義

     write = 0x2, // 重定義

如果這一點沒有讓你惱怒過的話,你可能還沒寫過多少 c++ 代碼 :-)。實際上,在最新的 c++0x 标準草案中有關于枚舉作用域問題的提案,但最終的解決方案會是怎樣的就無法未蔔先知了,畢竟對于象 c++ 這樣使用廣泛的語言來說,任何特性的增删和修改都必須十分小心謹慎。

當然,我們可以使用一些迂回的方法來解決這個問題(c++ 總是能給我們很多驚喜和意外)。例如,我們可以把枚舉值放在一個結構裡,并使用運算符重載來逼近枚舉的特性:

struct fileaccess {

     enum __enum {

         read = 0x1,

         write = 0x2

     };

     __enum _value; // 枚舉值

     fileaccess(int value = 0) : _value((__enum)value) {}

     fileaccess& operator=(int value) {

         this->_value = (__enum)value;

         return *this;

     }

     operator int() const {

         return this->_value;

我們現在可以按照希望的方式使用這個枚舉類型:

fileaccess access = fileaccess::read;

并且,因為我們提供了到 int 類型的轉換運算符,是以在需要 int 的地方都可以使用它,例如 switch 語句:

switch (access) {

     case fileaccess::read:

         break;

     case fileaccess::write:

}

當然我們不願意每次都手工編寫這樣的結構。通過使用宏,我們可以很容易做到這一點:

#define declare_enum(e) \

struct e \

{ \

public: \

     e(int value = 0) : _value((__enum)value) { \

     } \

     e& operator=(int value) { \

         this->_value = (__enum)value; \

         return *this; \

     operator int() const { \

         return this->_value; \

\

#define end_enum() \

     }; \

private: \

     __enum _value; \

我們現在可以按如下的方式定義前面的枚舉,并且不比直接寫 enum 複雜多少。

declare_enum(fileaccess)

end_enum()

declare_enum(fileshare)

繼續閱讀