C++11的enum class & enum struct和enum

C++11的enum class & enum struct和enumios

 

C++標準文檔——n2347(學習筆記) 
連接:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2347.pdf程序員

 

 

1. 舊版enum存在的問題

問題 描述
1 向整形的隱式轉換(Implicit conversion to an integer)
2 沒法指定底層所使用的數據類型(Inability to specify underlying type)
3 enum的做用域(Scope)
4 不一樣編譯器解決該問題的方法不統一

1.1 問題1:向整形的隱式轉換

在開始這個問題以前,咱們須要知道什麼是整形提高編程

查看以前的博文:C\C++中的整形提高安全

在看完什麼是整形提高以後,咱們開始這個問題:markdown

舊版enum其實並不具備很是徹底的類型安全(固然它也體現了必定的類型安全:1.禁止不一樣枚舉體之間的賦值 2.禁止整形向枚舉體的隱式轉換等),也就是面對整形提高,舊版enum是沒有抗拒力的。less

例如:函數

#include <iostream> enum colorA{redA,greenA,grayA}; enum colorB {redB,greenB,yellowB}; void test(int data) { std::cout << "test called" << std::endl; } int main() { colorA ca(redA); colorB cb(greenB); //ca = cb; ERROR , 沒法從「colorB」轉換爲「colorA」 //ca = 2; ERROR , 沒法從「int」轉換爲「colorA」 bool res(ca < cb); //OK std::cout << std::boolalpha << res << std::endl; test(ca); //OK std::cin.get(); return 0; }

 

運行結果:學習

true 
test calledui

就像上面的代碼:咱們仍然能夠比較兩個不一樣枚舉體的大小,用枚舉體調用參數爲int的函數。顯然此時的枚舉體發生了 整形提高 。this


在沒法使用C++11新版enum的狀況下,機制的程序員想到了:將enum封裝到類的內部的方法。

#include <iostream> class Color { private: enum _color { _red, _blue, _yellow, _black }; public: explicit Color(const _color & other) { value = value; } explicit Color(const Color & other) { value = other.value; } const Color& operator=(const Color& other) { value = other.value; return *this; } static const Color red, blue, yellow, black; _color value; //operators bool operator <(const Color & other) { return value < other.value; } bool operator >(const Color & other) { return value > other.value; } bool operator <=(const Color & other) { return value <= other.value; } bool operator >=(const Color & other) { return value >= other.value; } bool operator ==(const Color & other) { return value == other.value; } //... //conversion int toint() { return value; } }; //init static const Color obj const Color Color::red(Color::_color::_red); const Color Color::blue(Color::_color::_blue); const Color Color::yellow(Color::_color::_yellow); const Color Color::black(Color::_color::_black); void test(int data) { std::cout << "called" << std::endl; } int main() { Color ca(Color::blue); std::cout << ca.toint() << std::endl; //ca = 2; ERROR, 沒有找到接受「int」類型的右操做數的運算符(或沒有可接受的轉換) //test(ca); ERROR, 沒法將參數 1 從「Color」轉換爲「int」 //bool res(ca > 2); ERROR,沒有找到接受「int」類型的右操做數的運算符(或沒有可接受的轉換) std::cin.get(); return 0; }

 


的確,封裝在類中的enum可以抵抗整形提高。可是這種enum不一樣於POD(plain old data),沒法放入寄存器當中,這會帶來額外的開銷。


1.2 問題2:沒法指定底層所使用的數據類型

A. 首先,沒法指定數據類型,致使咱們沒法明確枚舉類型所佔的內存大小。這種麻煩在結構體當中尤其突出,特別是當咱們須要內存對齊和填充處理的時候。

#include <iostream> enum Version { Ver1 = 1, Ver2, Ver3 }; struct MyStruct { MyStruct(Version ver) { this->Ver = ver; } Version Ver; //Ohters... }; int main() { MyStruct m(Version::Ver1); std::cin.get(); return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

此時咱們的解決辦法仍是:不使用enum

#include <iostream> enum Version { Ver1 = 1, Ver2, Ver3 }; struct MyStruct { MyStruct(Version ver) { this->Ver = ver; } unsigned char Ver;//將enum Version轉爲unsigned char類型 //Ohters... }; int main() { MyStruct m(Version::Ver1); std::cin.get(); return 0; }

 

B. 其次,當咱們使用enum時,咱們沒法決定編譯器底層是如何對待enum的(好比:signed和unsigned)。

#include <iostream> enum MyEnum { num1 = 1, num2 = 2, numn = 0xFFFFFF00U }; int main() { std::cout << num1 << std::endl; std::cout << num2 << std::endl; std::cout << numn << std::endl; std::cin.get(); return 0; }

 

VS2015運行結果:



-256

CodeBlocks運行結果:



4294967040


在 numn=0xFFFFFF00U;中,咱們但願0xFFFFFF00表現爲unsigned。可是不一樣的編譯器其標準也不一樣。這就給咱們的程序帶來了許多的不肯定性。


在文檔n2347中的例子:不一樣編譯器對0xFFFFFFF0U的表現。

#include <iostream> using namespace std; enum E { E1 = 1, E2 = 2, Ebig = 0xFFFFFFF0U }; int main() { cout << sizeof(E) << endl; cout << "Ebig = " << Ebig << endl; cout << "E1 ? -1 =\t" << (E1 < -1 ? "less" : E1 > -1 ? "greater" : "equal") << endl; cout << "Ebig ? -1 =\t" << (Ebig < -1 ? "less" : Ebig > -1 ? "greater" : "equal") << endl; }

 

這裏寫圖片描述


1.3 問題3:enum的做用域

enum的中的 」 { } 」 大括號並無將枚舉成員的可見域限制在大括號內,致使enum成員曝露到了上一級做用域(塊語句)中。

例如:

#include <iostream> enum color{red,blue};//定義擁有兩個成員的enum,red和blue在enum的大括號外部能夠直接訪問,而不須要使用域運算符。 int main() { std::cout << blue << std::endl; std::cin.get(); return 0; }

 

 

運行結果:

1

-

就如上面的代碼,咱們能夠在blue的大括號以外訪問它,color的成員被泄露到了該文件的全局做用域中(雖然它尚不具有外部連接性)。能夠直接訪問,而不須要域運算符的幫助。


可是這不是關鍵,有時咱們反而以爲很是方便。下面纔是問題所在:

  • 問題:沒法定義同名的枚舉成員
enum color { red, blue }; //enum MyEnum { red, yellow }; ERROR, 重定義;之前的定義是「枚舉數」

 

如上面的代碼所示:咱們沒法重複使用red這個標識符。由於它在color中已經被用過了。可是,它們明明就是不一樣的枚舉類型,若是可使用相同的成員名稱,而後經過域運算符來訪問的話,該有多好!就像下面這樣:

color::red

可是這是舊版的enum沒法作到的。


  • 解決上述問題:利用命名空間
#include <iostream> namespace spaceA { enum color { red, blue }; } namespace spaceB { enum colorX { red, blue }; } int main() { std::cout << spaceA::red << std::endl; std::cout << spaceB::blue << std::endl; std::cout << std::boolalpha << (spaceA::red > spaceB::blue) << std::endl; std::cin.get(); return 0; }

 

運行結果:



false

-

是的,只要利用命名空間咱們就能解決枚舉體的成員重定義問題,可是添加了多餘的一層命名空間,未免顯得麻煩


1.4 不一樣編譯器解決該問題的方法不統一

在1.2中展現的圖片告訴咱們:有些編譯器可能提供了相應的擴展來解決這些問題,可是有的編譯器卻沒有,這使得咱們的編程很是的不統一,有時候由於enum而削弱了程序的可移植性。


2. enum class 和 enum struct


2.1 enum class 和 enum struct 是等價的


2.2 聲明

如大標題,枚舉體的聲明和定義使用 enum class或是enum struct, 兩者是等價的。使用enum class\enum struct不會與現存的enum關鍵詞衝突。並且enum class\enum struct具備更好的類型安全和相似封裝的特性(scoped nature)。

enum class color{red,green,yellow}; 
enum class colorx{red,green=100,yellow}; 
//....


2.3 類型轉換

與整形之間不會發生隱式類型轉換,可是能夠強轉。

#include <iostream> enum class color { red, green, yellow}; int main() { //int res(color::red); //ERROR , 「初始化」: 沒法從「color」轉換爲「int」 //color col(2);//ERROR , 「初始化」: 沒法從「int」轉換爲「color」 //強轉 int res(static_cast<int>(color::red));//OK color col(static_cast<color>(1));//OK std::cin.get(); return 0; }

 


2.4 指定底層數據類型(underlying type)

默認的底層數據類型是int,用戶能夠經過:type(冒號+類型)來指定任何整形(除了wchar_t)做爲底層數據類型。

enum class color:unsigned char{red,blue}; enum calss colorb:long long{yellow,black};

 


2.5 域

引入了域,要經過域運算符訪問,不能夠直接經過枚舉體成員名來訪問(因此咱們能夠定義相同的枚舉體成員而不會發生重定義的錯誤)

#include <iostream> enum class color { red, green, yellow}; enum class colorX { red, green, yellow }; int main() { //使用域運算符訪問枚舉體成員,強轉後打印 std::cout << static_cast<int>(color::red) << std::endl; std::cout << static_cast<int>(colorX::red) << std::endl; std::cin.get(); return 0; }

 

 

運行結果:


0


3. C++11enum的一些新特色

  • 枚舉體的定義和聲明問題 
    這裏寫圖片描述
  • 用enum定義的枚舉體是一個不具備封裝性(不知道如何翻譯是好:unscoped enumeration)的枚舉體,他的成員能夠在enum的大括號外被直接訪問。而用enum class或是enum struct(兩者在語法上是等價的)定義的枚舉體是具備封裝性的(scoped enumeration),他的成員同過成員名直接訪問,而應經過域運算符來訪問。
#include <iostream> enum class color{red,black}; enum colorx{green,yellow}; int main() { color::red;//用域運算符訪問color的成員 green;//直接訪問colorx的成員 colorx::green;//用域運算符訪問colorx的成員 std::cin.get(); return 0; }

 

  • 圖中的enum-base應該只能是整形的數據(不能是浮點類型或是其餘類型),const或是volatile會被忽略。
  • enumerator-list中的成員被做爲常量使用,與常量的功能等價。
  • 使用=給成員初始化的時候,=右邊應該使用常量,這個常量應該爲整形或是其餘的枚舉類型。若是第一個枚舉成員沒有初始化,那麼他默認爲0,其餘沒有初始化的成員是前面一個成員的值加1。
1enum color { red=3, black, gray };//成員的值分別爲:3 4 5 enum colorx { green, yellow };//成員的值分別爲:0 1 enum colorxx{xred,xyellow,xblack=12,xgray};//成員的值分別爲:0 1 12 13

 

  • 每種枚舉體的類型都不一樣於其餘枚舉體。
enum colora{red};//colora的類型與colorb的類型不一樣 enum colorb{yellow};

 

  • 每種枚舉都具備底層數據類型,同過:type(冒號+類型)來指定。對於指定了數據類型的枚舉體,他的數據類型爲指定的數據類型。若是沒有固定的底層數據類型:

    1. 對於enum class和enum struct來講,他的底層數據類型是int。
    2. 對於enum來講,他的底層數據類型根據編譯器而不一樣。
    3. 若是有使用數據初始化,那麼他的數據類型與用來初始化的數據的類型相同。
  • 若是該枚舉體沒有指定的底層數據類型,並且該枚舉體的成員爲空,那麼這個枚舉體至關於只有一個成員0

  • enum(非enum class\enum struct)會發生整形提高
#include <iostream> enum color { red, green, yellow }; int main() { std::cout << std::boolalpha << (red == green) << std::endl;//(red == green)發生了整形提高 std::cin.get(); return 0; }

 

  • enum(非enum class\enum struct)會發生自動數據類型轉換。可是enum class\enum struct是不容許這麼作的。
#include <iostream> enum color { red, green, yellow }; int main() { //color col = 2;//ERROR , 「初始化」: 沒法從「int」轉換爲「color」 int i = green;//發生隱式轉換 std::cout << i << std::endl; std::cin.get(); return 0; }

 

運行結果:

1

  • 能夠對enum和enum class\enum struct進行強制轉換。
#include <iostream> enum class color { red, green, yellow,a,b,v }; int main() { int res(0); //res = color::red + color::green;//ERROR , 「color」不定義該運算符或到預約義運算符可接收的類型的轉換 res = static_cast<int>(color::red) + static_cast<int>(color::green); std::cout << res << std::endl; std::cin.get(); return 0; }

 

運行結果:

1


3. 轉載請註明出處

 
相關文章
相關標籤/搜索