結(jié)合C++11的新特性來解析C++中的枚舉與聯(lián)合
枚舉
枚舉是用戶定義的類型,其中包含一組稱為枚舉器的命名的整型常數(shù)。
語法
// 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;
參數(shù)
identifier
指定給與枚舉的類型名稱。
type
枚舉器的基礎(chǔ)類型;所有枚舉器都具有相同的基礎(chǔ)類型。可能是任何整型。
enum-list
枚舉中以逗號分隔的枚舉器列表。范圍中的每個枚舉器或變量名必須是唯一的。但是,值可以重復(fù)。在未區(qū)分范圍的枚舉中,范圍是周邊范圍;在區(qū)分范圍的枚舉中,范圍是 enum-list 本身。
class
可使用聲明中的此關(guān)鍵字指定枚舉區(qū)分范圍,并且必須提供 identifier。還可使用 struct 關(guān)鍵字來代替 class,因為在此上下文中它們在語義上等效。
備注
枚舉提供上下文來描述以命名常數(shù)表示的一系列值,這些值也稱為枚舉器。在原始 C 和 C++ 枚舉類型中,非限定枚舉器在聲明枚舉的整個范圍中可見。在區(qū)分范圍的枚舉中,枚舉器名稱必須由枚舉類型名稱限定。以下示例演示兩種枚舉之間的基本差異:
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
{ /*...*/
}
}
}
將為枚舉中的每個名稱分配一個整數(shù)值,該值與其在枚舉中的順序相對應(yīng)。默認(rèn)情況下,為第一個值分配 0,為下一個值分配 1,以此類推,但你可以顯式設(shè)置枚舉器的值,如下所示:
enum Suit { Diamonds = 1, Hearts, Clubs, Spades };
為枚舉器 Diamonds 分配值 1。后續(xù)枚舉器接收的值會在前一個枚舉器的值的基礎(chǔ)上加一(如果沒有顯式賦值)。在前面的示例中,Hearts 將具有值 2,Clubs 將具有值 3,依此類推。
每個枚舉器將被視為常數(shù),并且必須在定義 enum 的范圍內(nèi)(對于未區(qū)分圍的枚舉)或在枚舉本身中(對于區(qū)分范圍的枚舉)具有唯一名稱。為這些名稱指定的值不必是唯一的。例如,如果一個未區(qū)分范圍的枚舉 Suit 的聲明如下:
enum Suit { Diamonds = 5, Hearts, Clubs = 4, Spades };
Diamonds、Hearts、Clubs 和 Spades 的值分別是 5、6、4 和 5。請注意,5 使用了多次;盡管這并不符合預(yù)期,但是允許的。對于區(qū)分范圍的枚舉來說,這些規(guī)則是相同的。
強(qiáng)制轉(zhuǎn)換規(guī)則
未區(qū)分范圍的枚舉常數(shù)可以隱式轉(zhuǎn)換為 int,但是 int 不可以隱式轉(zhuǎn)換為枚舉值。下面的示例顯示了如果嘗試為 hand 分配一個不是 Suit 的值可能出現(xiàn)的情況:
int account_num = 135692; Suit hand; hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'
將 int 轉(zhuǎn)換為區(qū)分范圍或未區(qū)分范圍的枚舉器時,需要強(qiáng)制轉(zhuǎn)換。但是,你可以將區(qū)分范圍的枚舉器提升為整數(shù)值,而不進(jìn)行強(qiáng)制轉(zhuǎn)換。
int account_num = Hearts; //OK if Hearts is in a unscoped enum
按照這種方式使用隱式轉(zhuǎn)換可能導(dǎo)致意外副作用。若要幫助消除與區(qū)分范圍的枚舉相關(guān)的編程錯誤,區(qū)分范圍的枚舉值必須是強(qiáng)類型值。區(qū)分范圍的枚舉器必須由枚舉類型名稱(標(biāo)識符)限定,并且無法進(jìn)行隱式轉(zhuǎn)換,如以下示例所示:
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; 行仍會導(dǎo)致對未區(qū)分范圍的枚舉發(fā)生的錯誤,如前面所示。它可以與顯式強(qiáng)制轉(zhuǎn)換一起使用。但是,借助區(qū)分范圍的枚舉,不再允許在沒有顯式強(qiáng)制轉(zhuǎn)換的情況下在下一條語句 account_num = Suit::Hearts; 中嘗試轉(zhuǎn)換。
聯(lián)合
union 是用戶定義的類型,其中所有成員都共享同一個內(nèi)存位置。 這意味著在任何給定時間,聯(lián)合都不能包含來自其成員列表的多個對象。 這還意味著無論聯(lián)合具有多少成員,它始終僅使用足以存儲最大成員的內(nèi)存。
具有大量對象和/或內(nèi)存有限時,聯(lián)合可用于節(jié)省內(nèi)存。 但是,需要格外小心才能正確使用它們,因為由你負(fù)責(zé)確??墒冀K訪問寫入的最后一個成員。 如果任何成員類型具有不常用構(gòu)造函數(shù),則必須編寫附加代碼來顯式構(gòu)造和銷毀該成員。 使用聯(lián)合之前,應(yīng)考慮是否可以使用基類和派生類來更好地表示嘗試解決的問題。
union [name] { member-list };
參數(shù)
name
為聯(lián)合提供的類型名稱。
member-list
聯(lián)合可以包含的成員。 請參閱“備注”。
備注
聲明聯(lián)合
利用 union 關(guān)鍵字開始聯(lián)合的聲明,并用大括號包含成員列表:
// 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
}
使用聯(lián)合
在前面的示例中,任何訪問聯(lián)合的代碼都需要了解保存數(shù)據(jù)的成員。 此問題最常見的解決方案是將聯(lián)合以及其他枚舉成員(指示當(dāng)前存儲在聯(lián)合中的數(shù)據(jù)的類型)放入一個結(jié)構(gòu)中。 這稱為可區(qū)分的聯(lián)合,下面的示例演示了基本模式。
#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 結(jié)構(gòu)中的聯(lián)合沒有名稱。 這是匿名聯(lián)合,可以訪問其成員,如同它們是結(jié)構(gòu)的直接成員一樣。 有關(guān)匿名聯(lián)合的詳細(xì)信息,請參閱下面一節(jié)。
當(dāng)然,上面的示例演示的問題也可以通過以下方法解決:使用派生自公共基類的類,并基于容器中每個對象的運行時類型對代碼進(jìn)行分支。 這可以生成更易于維護(hù)和理解的代碼,但是也可能比使用聯(lián)合更慢。 此外,通過聯(lián)合可以存儲完全不相關(guān)的類型,并動態(tài)更改存儲的值的類型,而無需更改聯(lián)合變量本身的類型。 因此可以創(chuàng)建其元素存儲不同類型的不同值的 MyUnionType 異類數(shù)組。
請注意,可能會很容易誤用前面示例中的 Input 結(jié)構(gòu)。 完全由用戶負(fù)責(zé)正確使用鑒別器來訪問保存數(shù)據(jù)的成員。 你可以通過使聯(lián)合成為專用并提供特殊訪問函數(shù)(如下一個示例所示)來防止誤用。
無限制的聯(lián)合 (C++11)
在 C++03 及更低版本中,聯(lián)合可以包含具有類類型的非靜態(tài)數(shù)據(jù)成員,只要該類型沒有用戶提供的構(gòu)造函數(shù)、析構(gòu)函數(shù)或賦值運算符即可。 在 C++11 中,消除了這些限制。 如果在聯(lián)合中包含這樣一個成員,則編譯器會自動將不是用戶提供的任何特殊成員函數(shù)標(biāo)記為已刪除。 如果聯(lián)合是類或結(jié)構(gòu)中的匿名聯(lián)合,則類或結(jié)構(gòu)的不是用戶提供的任何特殊成員函數(shù)都會標(biāo)記為已刪除。 下面的示例演示如何處理聯(lián)合的某個成員具有需要此特殊處理的成員的情況:
// 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;
};
};
聯(lián)合不能存儲引用。 聯(lián)合不支持繼承,因此聯(lián)合本身不能用作基類、繼承自另一個類或具有虛函數(shù)。
初始化聯(lián)合
可以通過指定包含在括號中的表達(dá)式來在相同語句中聲明并初始化聯(lián)合。 計算該表達(dá)式并將其分配給聯(lián)合的第一個字段。
#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 聯(lián)合排列在內(nèi)存中(概念性的),如下圖所示。
匿名聯(lián)合
匿名聯(lián)合是聲明的沒有 class-name 或 declarator-list 的聯(lián)合。
union { member-list }
匿名聯(lián)合中聲明的名稱可直接使用,就像非成員變量一樣。 因此,匿名聯(lián)合中聲明的名稱必須在周邊范圍中是唯一的。
除了聯(lián)合成員數(shù)據(jù)中列出的限制之外,匿名聯(lián)合還受其他限制:
如果在文件或命名空間范圍內(nèi)聲明聯(lián)合,則還必須將它們聲明為“靜態(tài)的”。
它們可以只具有公共成員;匿名聯(lián)合中的私有成員和受保護(hù)的成員會生成錯誤。
它們不能具有函數(shù)成員。
上一篇:剖析C++編程中friend關(guān)鍵字所修飾的友元函數(shù)和友元類
欄 目:C語言
下一篇:解析C++編程中virtual聲明的虛函數(shù)以及單個繼承
本文標(biāo)題:結(jié)合C++11的新特性來解析C++中的枚舉與聯(lián)合
本文地址:http://www.jygsgssxh.com/a1/Cyuyan/2533.html
您可能感興趣的文章
- 04-02c語言的正則匹配函數(shù) c語言正則表達(dá)式函數(shù)庫
- 04-02c語言中對數(shù)函數(shù)的表達(dá)式 c語言中對數(shù)怎么表達(dá)
- 04-02C語言中怎么打出三角函數(shù) c語言中怎么打出三角函數(shù)的值
- 01-10c語言求1+2+...+n的解決方法
- 01-10求子數(shù)組最大和的解決方法詳解
- 01-10深入理解約瑟夫環(huán)的數(shù)學(xué)優(yōu)化方法
- 01-10深入二叉樹兩個結(jié)點的最低共同父結(jié)點的詳解
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計- 解析最少換車次數(shù)的問題詳解
- 01-10c語言 跳臺階問題的解決方法
- 01-10如何判斷一個數(shù)是否為2的冪次方?若是,并判斷出來是多少次方


閱讀排行
本欄相關(guān)
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達(dá)
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對數(shù)函數(shù)的表達(dá)式 c語言中對
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機(jī)閱讀
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 04-02jquery與jsp,用jquery
- 01-11ajax實現(xiàn)頁面的局部加載
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 01-10C#中split用法實例總結(jié)
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-10delphi制作wav文件的方法
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文


