聯合C++11的新特征來解析C++中的列舉與結合。本站提示廣大學習愛好者:(聯合C++11的新特征來解析C++中的列舉與結合)文章只能為提供參考,不一定能成為您想要的結果。以下是聯合C++11的新特征來解析C++中的列舉與結合正文
列舉
列舉是用戶界說的類型,個中包括一組稱為列舉器的定名的整型常數。
語法
// unscoped enum: enum [identifier] [: type] {enum-list}; // scoped enum: enum [class|struct] [identifier] [: type] {enum-list}; // Forward declaration of enumerations (C++11): enum A : int; // non-scoped enum must have type specified enum class B; // scoped enum defaults to int enum class C : short;
參數
identifier
指定授與列舉的類型稱號。
type
列舉器的基本類型;一切列舉器都具有雷同的基本類型。能夠是任何整型。
enum-list
列舉中以逗號分隔的列舉器列表。規模中的每一個列舉器或變量名必需是獨一的。然則,值可以反復。在未辨別規模的列舉中,規模是周邊規模;在辨別規模的列舉中,規模是 enum-list 自己。
class
可以使用聲明中的此症結字指定列舉辨別規模,而且必需供給 identifier。還可以使用 struct 症結字來取代 class,由於在此高低文中它們在語義上等效。
備注
列舉供給高低文來描寫以定名常數表現的一系列值,這些值也稱為列舉器。在原始 C 和 C++ 列舉類型中,非限制列舉器在聲明列舉的全部規模中可見。在辨別規模的列舉中,列舉器稱號必需由列舉類型稱號限制。以下示例演示兩種列舉之間的根本差別:
namespace CardGame_Scoped { enum class Suit { Diamonds, Hearts, Clubs, Spades }; void PlayCard(Suit suit) { if (suit == Suit::Clubs) // Enumerator must be qualified by enum type { /*...*/} } } namespace CardGame_NonScoped { enum Suit { Diamonds, Hearts, Clubs, Spades }; void PlayCard(Suit suit) { if (suit == Clubs) // Enumerator is visible without qualification { /*...*/ } } }
將為列舉中的每一個稱號分派一個整數值,該值與其在列舉中的次序絕對應。默許情形下,為第一個值分派 0,為下一個值分派 1,以此類推,但你可以顯式設置列舉器的值,以下所示:
enum Suit { Diamonds = 1, Hearts, Clubs, Spades };
為列舉器 Diamonds 分派值 1。後續列舉器吸收的值會在前一個列舉器的值的基本上加一(假如沒有顯式賦值)。在後面的示例中,Hearts 將具有值 2,Clubs 將具有值 3,依此類推。
每一個列舉器將被視為常數,而且必需在界說 enum 的規模內(關於未辨別圍的列舉)或在列舉自己中(關於辨別規模的列舉)具有獨一稱號。為這些稱號指定的值不用是獨一的。例如,假如一個未辨別規模的列舉 Suit 的聲明以下:
enum Suit { Diamonds = 5, Hearts, Clubs = 4, Spades };
Diamonds、Hearts、Clubs 和 Spades 的值分離是 5、6、4 和 5。請留意,5 應用了屢次;雖然這其實不相符預期,然則許可的。關於辨別規模的列舉來講,這些規矩是雷同的。
強迫轉換規矩
未辨別規模的列舉常數可以隱式轉換為 int,然則 int 弗成以隱式轉換為列舉值。上面的示例顯示了假如測驗考試為 hand 分派一個不是 Suit 的值能夠湧現的情形:
int account_num = 135692; Suit hand; hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'
將 int 轉換為辨別規模或未辨別規模的列舉器時,須要強迫轉換。然則,你可以將辨別規模的列舉器晉升為整數值,而不停止強迫轉換。
int account_num = Hearts; //OK if Hearts is in a unscoped enum
依照這類方法應用隱式轉換能夠招致不測反作用。若要贊助清除與辨別規模的列舉相干的編程毛病,辨別規模的列舉值必需是強類型值。辨別規模的列舉器必需由列舉類型稱號(標識符)限制,而且沒法停止隱式轉換,如以下示例所示:
namespace ScopedEnumConversions { enum class Suit { Diamonds, Hearts, Clubs, Spades }; void AttemptConversions() { Suit hand; hand = Clubs; // error C2065: 'Clubs' : undeclared identifier hand = Suit::Clubs; //Correct. int account_num = 135692; hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit' hand = static_cast<Suit>(account_num); // OK, but probably a bug!!! account_num = Suit::Hearts; // error C2440: '=' : cannot convert from 'Suit' to 'int' account_num = static_cast<int>(Suit::Hearts); // OK }
留意,hand = account_num; 行仍會招致對未辨別規模的列舉產生的毛病,如後面所示。它可以與顯式強迫轉換一路應用。然則,借助辨別規模的列舉,不再許可在沒有顯式強迫轉換的情形下鄙人一條語句 account_num = Suit::Hearts; 中測驗考試轉換。
結合
union 是用戶界說的類型,個中一切成員都同享統一個內存地位。 這意味著在任何給准時間,結合都不克不及包括來自其成員列表的多個對象。 這還意味著不管結合具有若干成員,它一直僅應用足以存儲最年夜成員的內存。
具有年夜量對象和/或內存無限時,結合可用於節儉內存。 然則,須要非分特別當心能力准確應用它們,由於由你擔任確保可一直拜訪寫入的最初一個成員。 假如任何成員類型具有不經常使用結構函數,則必需編寫附加代碼來顯式結構和燒毀該成員。 應用結合之前,應斟酌能否可使用基類和派生類來更好地表現測驗考試處理的成績。
union [name] { member-list };
參數
name
為結合供給的類型稱號。
member-list
結合可以包括的成員。 請參閱“備注”。
備注
聲明結合
應用 union 症結字開端結合的聲明,並用年夜括號包括成員列表:
// declaring_a_union.cpp union RecordType // Declare a simple union type { char ch; int i; long l; float f; double d; int *int_ptr; }; int main() { RecordType t; t.i = 5; // t holds an int t.f = 7.25 // t now holds a float }
應用結合
在後面的示例中,任何拜訪結合的代碼都須要懂得保留數據的成員。 此成績最多見的處理計劃是將結合和其他列舉成員(指導以後存儲在結合中的數據的類型)放入一個構造中。 這稱為可辨別的結合,上面的示例演示了根本形式。
#include "stdafx.h" #include <queue> using namespace std; enum class WeatherDataType { Temperature, Wind }; struct TempData { int StationId; time_t time; double current; double max; double min; }; struct WindData { int StationId; time_t time; int speed; short direction; }; struct Input { WeatherDataType type; union { TempData temp; WindData wind; }; }; // Functions that are specific to data types void Process_Temp(TempData t) {} void Process_Wind(WindData w) {} // Container for all the data records queue<Input> inputs; void Initialize(); int main(int argc, char* argv[]) { Initialize(); while (!inputs.empty()) { Input i = inputs.front(); switch (i.type) { case WeatherDataType::Temperature: Process_Temp(i.temp); break; case WeatherDataType::Wind: Process_Wind(i.wind); break; default: break; } inputs.pop(); } return 0; } void Initialize() { Input first, second; first.type = WeatherDataType::Temperature; first.temp = { 101, 1418855664, 91.8, 108.5, 67.2 }; inputs.push(first); second.type = WeatherDataType::Wind; second.wind = { 204,1418859354, 14, 27 }; inputs.push(second); }
在後面的示例中,請留意 Input 構造中的結合沒著名稱。 這是匿名結合,可以拜訪其成員,好像它們是構造的直接成員一樣。 有關匿名結合的具體信息,請參閱上面一節。
固然,下面的示例演示的成績也能夠經由過程以下辦法處理:應用派生自公共基類的類,並基於容器中每一個對象的運轉時類型對代碼停止分支。 這可以生成更容易於保護和懂得的代碼,然則也能夠比應用結合更慢。 另外,經由過程結合可以存儲完整不相干的類型,並靜態更改存儲的值的類型,而無需更改結合變量自己的類型。 是以可以創立其元素存儲分歧類型的分歧值的 MyUnionType 異類數組。
請留意,能夠會很輕易誤用後面示例中的 Input 構造。 完整由用戶擔任准確應用辨別器來拜訪保留數據的成員。 你可以經由過程使結合成為公用並供給特別拜訪函數(以下一個示例所示)來避免誤用。
無窮制的結合 (C++11)
在 C++03 及更低版本中,結合可以包括具有類類型的非靜態數據成員,只需該類型沒有效戶供給的結構函數、析構函數或賦值運算符便可。 在 C++11 中,清除了這些限制。 假如在結合中包括如許一個成員,則編譯器會主動將不是用戶供給的任何特別成員函數標志為已刪除。 假如結合是類或構造中的匿名結合,則類或構造的不是用戶供給的任何特別成員函數都邑標志為已刪除。 上面的示例演示若何處置結合的某個成員具有須要此特別處置的成員的情形:
// for MyVariant #include <crtdbg.h> #include <new> #include <utility> // for sample objects and output #include <string> #include <vector> #include <iostream> using namespace std; struct A { A() = default; A(int i, const string& str) : num(i), name(str) {} int num; string name; //... }; struct B { B() = default; B(int i, const string& str) : num(i), name(str) {} int num; string name; vector<int> vec; // ... }; enum class Kind { None, A, B, Integer }; #pragma warning (push) #pragma warning(disable:4624) class MyVariant { public: MyVariant() : kind_(Kind::None) { } MyVariant(Kind kind) : kind_(kind) { switch (kind_) { case Kind::None: break; case Kind::A: new (&a_) A(); break; case Kind::B: new (&b_) B(); break; case Kind::Integer: i_ = 0; break; default: _ASSERT(false); break; } } ~MyVariant() { switch (kind_) { case Kind::None: break; case Kind::A: a_.~A(); break; case Kind::B: b_.~B(); break; case Kind::Integer: break; default: _ASSERT(false); break; } kind_ = Kind::None; } MyVariant(const MyVariant& other) : kind_(other.kind_) { switch (kind_) { case Kind::None: break; case Kind::A: new (&a_) A(other.a_); break; case Kind::B: new (&b_) B(other.b_); break; case Kind::Integer: i_ = other.i_; break; default: _ASSERT(false); break; } } MyVariant(MyVariant&& other) : kind_(other.kind_) { switch (kind_) { case Kind::None: break; case Kind::A: new (&a_) A(move(other.a_)); break; case Kind::B: new (&b_) B(move(other.b_)); break; case Kind::Integer: i_ = other.i_; break; default: _ASSERT(false); break; } other.kind_ = Kind::None; } MyVariant& operator=(const MyVariant& other) { if (&other != this) { switch (other.kind_) { case Kind::None: this->~MyVariant(); break; case Kind::A: *this = other.a_; break; case Kind::B: *this = other.b_; break; case Kind::Integer: *this = other.i_; break; default: _ASSERT(false); break; } } return *this; } MyVariant& operator=(MyVariant&& other) { _ASSERT(this != &other); switch (other.kind_) { case Kind::None: this->~MyVariant(); break; case Kind::A: *this = move(other.a_); break; case Kind::B: *this = move(other.b_); break; case Kind::Integer: *this = other.i_; break; default: _ASSERT(false); break; } other.kind_ = Kind::None; return *this; } MyVariant(const A& a) : kind_(Kind::A), a_(a) { } MyVariant(A&& a) : kind_(Kind::A), a_(move(a)) { } MyVariant& operator=(const A& a) { if (kind_ != Kind::A) { this->~MyVariant(); new (this) MyVariant(a); } else { a_ = a; } return *this; } MyVariant& operator=(A&& a) { if (kind_ != Kind::A) { this->~MyVariant(); new (this) MyVariant(move(a)); } else { a_ = move(a); } return *this; } MyVariant(const B& b) : kind_(Kind::B), b_(b) { } MyVariant(B&& b) : kind_(Kind::B), b_(move(b)) { } MyVariant& operator=(const B& b) { if (kind_ != Kind::B) { this->~MyVariant(); new (this) MyVariant(b); } else { b_ = b; } return *this; } MyVariant& operator=(B&& b) { if (kind_ != Kind::B) { this->~MyVariant(); new (this) MyVariant(move(b)); } else { b_ = move(b); } return *this; } MyVariant(int i) : kind_(Kind::Integer), i_(i) { } MyVariant& operator=(int i) { if (kind_ != Kind::Integer) { this->~MyVariant(); new (this) MyVariant(i); } else { i_ = i; } return *this; } Kind GetKind() const { return kind_; } A& GetA() { _ASSERT(kind_ == Kind::A); return a_; } const A& GetA() const { _ASSERT(kind_ == Kind::A); return a_; } B& GetB() { _ASSERT(kind_ == Kind::B); return b_; } const B& GetB() const { _ASSERT(kind_ == Kind::B); return b_; } int& GetInteger() { _ASSERT(kind_ == Kind::Integer); return i_; } const int& GetInteger() const { _ASSERT(kind_ == Kind::Integer); return i_; } private: Kind kind_; union { A a_; B b_; int i_; }; }; #pragma warning (pop) int main() { A a(1, "Hello from A"); B b(2, "Hello from B"); MyVariant mv_1 = a; cout << "mv_1 = a: " << mv_1.GetA().name << endl; mv_1 = b; cout << "mv_1 = b: " << mv_1.GetB().name << endl; mv_1 = A(3, "hello again from A"); cout << R"aaa(mv_1 = A(3, "hello again from A"): )aaa" << mv_1.GetA().name << endl; mv_1 = 42; cout << "mv_1 = 42: " << mv_1.GetInteger() << endl; b.vec = { 10,20,30,40,50 }; mv_1 = move(b); cout << "After move, mv_1 = b: vec.size = " << mv_1.GetB().vec.size() << endl; cout << endl << "Press a letter" << endl; char c; cin >> c; } #include <queue> #include <iostream> using namespace std; enum class WeatherDataType { Temperature, Wind }; struct TempData { TempData() : StationId(""), time(0), current(0), maxTemp(0), minTemp(0) {} TempData(string id, time_t t, double cur, double max, double min) : StationId(id), time(t), current(cur), maxTemp(max), minTemp(0) {} string StationId; time_t time = 0; double current; double maxTemp; double minTemp; }; struct WindData { int StationId; time_t time; int speed; short direction; }; struct Input { Input() {} Input(const Input&) {} ~Input() { if (type == WeatherDataType::Temperature) { temp.StationId.~string(); } } WeatherDataType type; void SetTemp(const TempData& td) { type = WeatherDataType::Temperature; // must use placement new because of string member! new(&temp) TempData(td); } TempData GetTemp() { if (type == WeatherDataType::Temperature) return temp; else throw logic_error("Can't return TempData when Input holds a WindData"); } void SetWind(WindData wd) { // Explicitly delete struct member that has a // non-trivial constructor if (type == WeatherDataType::Temperature) { temp.StationId.~string(); } wind = wd; //placement new not required. } WindData GetWind() { if (type == WeatherDataType::Wind) { return wind; } else throw logic_error("Can't return WindData when Input holds a TempData"); } private: union { TempData temp; WindData wind; }; };
結合不克不及存儲援用。 結合不支撐繼續,是以結合自己不克不及用作基類、繼續自另外一個類或具有虛函數。
初始化結合
可以經由過程指定包括在括號中的表達式來在雷同語句中聲明並初始化結合。 盤算該表達式並將其分派給結合的第一個字段。
#include <iostream> using namespace std; union NumericType { short iValue; long lValue; double dValue; }; int main() { union NumericType Values = { 10 }; // iValue = 10 cout << Values.iValue << endl; Values.dValue = 3.1416; cout << Values.dValue) << endl; } /* Output: 10 3.141600 */
NumericType 結合分列在內存中(概念性的),以下圖所示。
匿名結合
匿名結合是聲明的沒有 class-name 或 declarator-list 的結合。
union { member-list }
匿名結合中聲明的稱號可直接應用,就像非成員變量一樣。 是以,匿名結合中聲明的稱號必需在周邊規模中是獨一的。
除結合成員數據中列出的限制以外,匿名結合還受其他限制:
假如在文件或定名空間規模內聲明結合,則還必需將它們聲明為“靜態的”。
它們可以只具有公共成員;匿名結合中的公有成員和受掩護的成員會生成毛病。
它們不克不及具有函數成員。