自定義類型

c++語言分兩部分,語言核心和標準庫
c++已經爲我們提供了一些基本類型,我們可以通過這些基本類型構建複雜的類型
c++提供的內置類型和操作非常豐富,也很low-level(基本:直接、高效,這些操作反應了計算機常規能力)
利用這些內置類型和操作 + 抽象機制 = everything

抽象機制是爲了讓程序員設計和實現自定義的類型(有合適的表示和操作),並且可以非常簡單非常優雅地使用這個類型。除了內置類型之外,利用抽象機制創建的類型稱爲用戶自定義類型。簡稱類、枚舉

大部分c++書籍都是告訴閱讀者 如何設計 實現 使用 自定義類型

結構 struct

構建自定義類型的第一步
將幾個類型丟在一個數據結構:struct
訪問一個struct的元素用. dot 或是->

類 class

第二步
爲幾個數據增加幾個操作,這樣的優勢是:可以通過任意方式使用數據
對於自定義類型來說 有一套嚴格聯繫存在於表現和操作中,這是爲了讓這個自定義類型擁有更多的屬性,讓她看起來像一個真正的實在的類型。對於用戶來說(這裏的用戶指使用這個類型的程序員),我們希望用戶不能訪問表現(這裏以及上面的表現就是類的具體實現),而是使用類型,這裏的解釋是:一個類型有接口和實現,接口(對應上面的“操作”)被用戶使用,實現是類型提供者的工作,對應上面的表現。這樣的語言機制稱爲class。一個類應該有一些成員(數據 函數 或者是類型成員);接口部分應該是public成員,private成員應該只能被接口訪問。
如果一個成員函數的名稱和類名一樣,這個函數就被稱爲構造函數,這個構造函數用來初始化這個類型的對象,這樣我們就不用擔心類型中變量未初始化的問題。

聯合 unions

unions是一種特殊的struct,她的所有的成員都放在同一個內存中,大小是所有元素中最大的那個,每一次 unions只保存一個成員的數據

在c++中的某些特殊場景下使用unions是非常好的選擇,首選需要說明 unions使用的場景:節約空間。這是c++之父的原話:“space is wasted”所以針對這種情況應優先使用unions。使用的時候極容易出錯,所以最好將unions封裝一下,若非要使用裸的unions,裏面的成員也應當儘量簡單
如何封裝:

unions MyType{
    char* s;
    int i;
}

struct MyType2{
    Type t;
    MyType v;
}
// ps:假如 當t爲一個值時v應取s;當t爲另一個值時v應取i

// 爲了保證每次都取到正確的值,在操作的時候應該特別注意:
void fun(MyType2* p){
    if (p->t == xx)
        p->v.s
    else if (p->t == xxx)
        p->v.i
}
// 如果是要封裝,最好如上面一樣 弄一個變量t來確保每次的對應關係是正確的

枚舉 enum

除了class外,c++提供了一種簡單的枚舉值的自定義類型:enum

什麼是enum?(或者說有啥規則)
enum裏包含了一組值,這些值可以轉換成int類型
需要注意的是:可能每個值都可能一樣

先來看看enum的語法:
enum-key attr(optional) identifier(optional) enum-base(optional) { enumerator-list(optional) } (1)
enum-key attr(optional) nested-name-specifier(optional) identifier enum-base(optional) ; (2) (since C++11)

好吧,第一種是我們常見的寫法;第二種是新標準加入的,是一種不透明的聲明?啥意思:就是隻定義了enum類型,然而並沒有枚舉值

具體分析一下上面的語法:
enum-key:三種可選值:enum、enum class 、enum struct,我們一般使用的是第一種, 後兩種是新標準加的
attr:可選的,任意屬性的序列
identifier:可選的,枚舉類型的名稱
enum-base:指定枚舉值的類型,這個類型應該是可以和int兼容的,eg:long short longlong,如果不指定顯示,默認就是int,形式如 enum abc:long{a,b};
enumerator-list:枚舉值列表

語法簡單介紹了一下,接下來介紹兩種不同的枚舉:
在介紹之前,先了解一下以前enum的不足(如果沒有不足,也不會有新標準中添加的東西了)
“類型是int;可見範圍:整個enum聲明的作用域(容易造成命名衝突);不支持前置聲明”
新標準將enum分成了兩種不同的枚舉:帶範圍限定的枚舉和不帶範圍限定的枚舉。
還是用英文描述準確一些:

unscoped enumeration
當語法中的enum-key指定爲enum時就是這類了
enum name { enumerator = constexpr , enumerator = constexpr , … } (1)
enum name : type { enumerator = constexpr , enumerator = constexpr , … } (2) (since C++11)
enum name : type ; (3) (since C++11)
第一種情況就是我們以前常用的,
第二種是限定了類型
第三種是前置聲明且指定了類型

scoped enumerations
當語法中的enum-key指定爲enum class 或是enum struct時就是這類了
enum struct|class name { enumerator = constexpr , enumerator = constexpr , … } (1)
enum struct|class name : type { enumerator = constexpr , enumerator = constexpr , … } (2)
enum struct|class name ; (3)
enum struct|class name : type ; (4)
第一種是帶範圍限定的
第二中帶類型
第三第四是前置聲明

新標準c++17讓我們可以爲enum添加一個初始化列表
使用初始化列表需要滿足以下幾個條件:
the initialization is direct-list-initialization
the initializer list has only a single element
the enumeration is either scoped or unscoped with underlying type fixed
the conversion is non-narrowing

enum byte : unsigned char {}; // byte is a new integer type
byte b { 42 }; // OK as of C++17 (direct-list-initialization)
byte c = { 42 }; // error
byte d = byte{ 42 }; // OK as of C++17; same value as b
byte e { -1 }; // error

struct A { byte b; };
A a1 = { { 42 } }; // error
A a2 = { byte{ 42 } }; // OK as of C++17

void f(byte);
f({ 42 }); // error

enum class Handle : std::uint32_t { Invalid = 0 };
Handle h { 42 }; // OK as of C++17

對於新標準之後的枚舉來說:
可以限定範圍、可以指定類型、可以用初始化列表
默認情況下,enum class 只有 賦值 初始化 比較操作,作爲自定義類型的一種,我們可以爲枚舉添加自己的操作:
eg:

enum class abc{
    a,
    b,
    c
};

abc& operator++(abc& t){
    switch (t) {
    case abc::a: return t = b;
    case abc::b: return t = c;
    case abc::c: return t = a;  
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章