结合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; 参数 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,Spades }; void PlayCard(Suit suit) { if (suit == Clubs) // Enumerator is visible without qualification { /*...*/ } } } 将为枚举中的每个名称分配一个整数值,该值与其在枚举中的顺序相对应。默认情况下,为第一个值分配 0,为下一个值分配 1,以此类推,但你可以显式设置枚举器的值,如下所示: enum Suit { Diamonds = 1,Spades }; 为枚举器 Diamonds 分配值 1。后续枚举器接收的值会在前一个枚举器的值的基础上加一(如果没有显式赋值)。在前面的示例中,Hearts 将具有值 2,Clubs 将具有值 3,依此类推。 enum Suit { Diamonds = 5,Clubs = 4,Spades }; Diamonds、Hearts、Clubs 和 Spades 的值分别是 5、6、4 和 5。请注意,5 使用了多次;尽管这并不符合预期,但是允许的。对于区分范围的枚举来说,这些规则是相同的。 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,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 }; 参数 // 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 结构中的联合没有名称。 这是匿名联合,可以访问其成员,如同它们是结构的直接成员一样。 有关匿名联合的详细信息,请参阅下面一节。 // 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,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 联合排列在内存中(概念性的),如下图所示。 匿名联合 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |