- 現(xiàn)代C++編程:從入門到實(shí)踐
- (美)喬什·洛斯皮諾索
- 2321字
- 2024-04-15 11:40:43
2.3 用戶自定義類型
用戶自定義類型(User-Defined Type)是用戶可以定義的類型。用戶自定義類型有三大類:
? 枚舉類型:最簡(jiǎn)單的用戶自定義類型。枚舉類型可以取的值被限制在一組可能的值中。枚舉類型是對(duì)分類概念進(jìn)行建模的最佳選擇。
? 類:功能更全面的類型,它使我們可以靈活地結(jié)合數(shù)據(jù)和函數(shù)。只包含數(shù)據(jù)的類被稱為普通數(shù)據(jù)類(Plain-Old-Data,POD),見2.3.2節(jié)。
? 聯(lián)合體:濃縮的用戶自定義類型。所有成員共享同一個(gè)內(nèi)存位置。聯(lián)合體本身很危險(xiǎn),容易被濫用。
2.3.1 枚舉類型
使用關(guān)鍵字enum class來(lái)聲明枚舉類型,關(guān)鍵字后面是類型名稱和它可以取的值的列表。這些值是任意的字母-數(shù)字字符串,代表任意想代表的類別。在實(shí)現(xiàn)內(nèi)部,這些值只是整數(shù),但它們?cè)试S使用程序員定義的類型而不是可能代表任何東西的整數(shù)來(lái)編寫更安全、更有表現(xiàn)力的代碼。例如,代碼清單2-13聲明了一個(gè)名為Race的枚舉類,它可以取七個(gè)值中的一個(gè)。
代碼清單2-13 一個(gè)包含尼爾·斯蒂芬森(Neal Stephenson)小說(shuō)《七夏娃》中所有種族的枚舉類

要將枚舉變量初始化為一個(gè)值,使用類型的名稱后跟兩個(gè)冒號(hào)::和所需的值即可實(shí)現(xiàn)。例如,下面的代碼展示了如何聲明變量langobard_race并將其初始化為Aidan:

注意 從技術(shù)上講,枚舉類是兩種枚舉類型中的一種:它被稱為作用域枚舉。為了與C語(yǔ)言兼容,C++也支持非作用域枚舉類型,它是用enum而非enum class聲明的。主要的區(qū)別是,作用域枚舉需要在值前面加上枚舉類型和::,而非作用域枚舉則不需要。非作用域枚舉類比作用域枚舉類使用起來(lái)更不安全,所以除非絕對(duì)必要,否則請(qǐng)不要使用它們。C++支持它們主要是出于歷史原因,特別是基于與C代碼的互操作。詳情請(qǐng)參見Scott Meyers的Effective Modern C++的第10項(xiàng)。
1.switch語(yǔ)句
switch語(yǔ)句根據(jù)condition(條件)值將控制權(quán)轉(zhuǎn)移到幾個(gè)語(yǔ)句中的一個(gè),condition值可以是整數(shù)或枚舉類型的。switch關(guān)鍵字表示一個(gè)switch語(yǔ)句。
switch語(yǔ)句提供了條件性分支。當(dāng)switch語(yǔ)句執(zhí)行時(shí),控制權(quán)將轉(zhuǎn)移到符合條件的情況(case語(yǔ)句),如果沒(méi)有符合條件表達(dá)式的情況,則轉(zhuǎn)移到默認(rèn)情況。每個(gè)case關(guān)鍵字都表示一種情況,而default關(guān)鍵字表示默認(rèn)情況。
有點(diǎn)令人困惑的是,執(zhí)行過(guò)程將持續(xù)到switch語(yǔ)句結(jié)束或break關(guān)鍵字。幾乎總能在每個(gè)條件的末尾發(fā)現(xiàn)一個(gè)break。
switch語(yǔ)句有很多case語(yǔ)句。代碼清單2-14顯示了它們是如何組合在一起的。
代碼清單2-14 switch工作框架


所有的switch語(yǔ)句都以switch關(guān)鍵字?開始,后面緊跟著用括號(hào)括起來(lái)的條件(condition)?。每個(gè)case語(yǔ)句都以case關(guān)鍵字?開頭,后面跟著枚舉值或整數(shù)值?。例如,如果條件值?等于case-a?,那么包含Handle case a here的代碼塊將被執(zhí)行。在每條case語(yǔ)句之后?,都要放置break關(guān)鍵字?。如果條件值與所有case中的值都不匹配,則執(zhí)行默認(rèn)的情況default?。
注意 每個(gè)case的大括號(hào)可有可無(wú),但強(qiáng)烈推薦使用。沒(méi)有它們,有時(shí)會(huì)得到令人驚訝的行為。
2.對(duì)枚舉類使用switch語(yǔ)句
代碼清單2-15對(duì)Race枚舉類使用switch語(yǔ)句來(lái)生成定制的問(wèn)候語(yǔ)。
代碼清單2-15 一個(gè)根據(jù)所選種族打印問(wèn)候語(yǔ)的程序


enum class?聲明了枚舉類型Race,我們可以用它將race初始化為Dinan?。switch語(yǔ)句?評(píng)估條件race,以確定將控制權(quán)交給哪個(gè)case語(yǔ)句。因?yàn)橐言谇懊鎸?b>race硬編碼為Dinan,因此將執(zhí)行第一條case語(yǔ)句?,它將打印You work hard.。第一條case語(yǔ)句后的break?將終止switch語(yǔ)句。
default語(yǔ)句?是一個(gè)安全功能。如果有人在枚舉類中添加了新的race值,那么在運(yùn)行時(shí)將檢測(cè)到這個(gè)未知的race,并打印出錯(cuò)誤信息。
試著把race?設(shè)置為不同的值,看看輸出有什么變化?
2.3.2 普通數(shù)據(jù)類
類是用戶自定義的包含數(shù)據(jù)和函數(shù)的類型,它們是C++的核心和靈魂。最簡(jiǎn)單的類是普通數(shù)據(jù)類(Plain-Old-Data,POD)。POD是簡(jiǎn)單的容器。我們可以把它們看作一種潛在的不同類型的元素的異構(gòu)數(shù)組。類的每個(gè)元素都被稱為一個(gè)成員(member)。
每個(gè)POD都以關(guān)鍵詞struct開頭,后面跟著POD的名稱,再后面要列出成員的類型和名稱。考慮下面這個(gè)有四個(gè)成員的Book類聲明:

Book包含一個(gè)名為name?的char數(shù)組、一個(gè)int year?、一個(gè)int pages?和一個(gè)bool hardcover?。
聲明POD變量就像聲明其他變量一樣:通過(guò)類型和名稱。我們可以使用點(diǎn)運(yùn)算符(.)訪問(wèn)變量的成員。
代碼清單2-16使用了Book類型。
代碼清單2-16 使用POD類Book來(lái)讀寫成員的例子

首先,聲明一個(gè)Book變量neuromancer?。然后,使用點(diǎn)運(yùn)算符(.)將neuro-mancer的頁(yè)數(shù)設(shè)置為271?。最后,打印一條信息,并從neuromancer中提取頁(yè)數(shù),同樣使用點(diǎn)運(yùn)算符?。
注意 POD有一些有用的底層特性:它們與C語(yǔ)言兼容,我們可以使用高效的機(jī)器指令來(lái)復(fù)制或移動(dòng)它們,而且它們可以在內(nèi)存中有效地表示出來(lái)。
C++保證成員在內(nèi)存中是按順序排列的,盡管有些實(shí)現(xiàn)要求成員沿著字的邊界對(duì)齊,這取決于CPU寄存器的長(zhǎng)度。一般來(lái)說(shuō),應(yīng)該在POD定義中從大到小排列成員。
2.3.3 聯(lián)合體
聯(lián)合體(union)類似于POD,它把所有的成員放在同一個(gè)地方。我們可以把聯(lián)合體看作對(duì)內(nèi)存塊的不同看法或解釋。它們?cè)谝恍┑讓忧闆r下是很有用的,例如,處理必須在不同架構(gòu)下保持一致的結(jié)構(gòu)時(shí),處理與C/C++互操作有關(guān)的類型檢查問(wèn)題時(shí),甚至在包裝位域(bitfield)時(shí)。
代碼清單2-17說(shuō)明了如何聲明聯(lián)合體:用union關(guān)鍵字代替struct即可。
代碼清單2-17 一個(gè)聯(lián)合體的例子

聯(lián)合體Variant可以被解釋成char[10]、int double。它占用的內(nèi)存與它最大的成員(在本例中可能是string)占用的內(nèi)存一樣多。
我們可以使用點(diǎn)運(yùn)算符(.)來(lái)指定聯(lián)合體的解釋。從語(yǔ)法上看,這看起來(lái)像訪問(wèn)POD的成員,但它在內(nèi)部是完全不同的。
因?yàn)槁?lián)合體的所有成員都在同一個(gè)地方,所以很容易造成數(shù)據(jù)損壞。代碼清單2-18說(shuō)明了這種危險(xiǎn)。
代碼清單2-18 使用代碼清單2-17中聯(lián)合體Variant的程序

首先,聲明Variant v?。接著,把v解釋為整數(shù),把它的值設(shè)置為42?,并打印它?。然后,把v重新解釋為浮點(diǎn)數(shù),重新賦值?,把它打印到控制臺(tái),一切看起來(lái)很好?。到目前為止還不錯(cuò)。
只有當(dāng)再次將v解釋為整數(shù)時(shí),災(zāi)難才會(huì)降臨?。在賦值為歐拉數(shù)?時(shí),會(huì)把v的原值(42)?給破壞了。
這就是聯(lián)合體存在的主要問(wèn)題:要靠程序員自己來(lái)跟蹤哪種解釋是合適的。編譯器不會(huì)提供幫助。
除了罕見的情況,應(yīng)該避免使用聯(lián)合體,本書中就不會(huì)使用它們。12.1.6節(jié)討論了當(dāng)需要多類型變量時(shí)應(yīng)選擇的一些更安全的選擇。
- Learn Type:Driven Development
- 測(cè)試驅(qū)動(dòng)開發(fā):入門、實(shí)戰(zhàn)與進(jìn)階
- AWS Serverless架構(gòu):使用AWS從傳統(tǒng)部署方式向Serverless架構(gòu)遷移
- PHP 7底層設(shè)計(jì)與源碼實(shí)現(xiàn)
- Visual C++數(shù)字圖像模式識(shí)別技術(shù)詳解
- OpenStack Cloud Computing Cookbook(Fourth Edition)
- Troubleshooting PostgreSQL
- 嚴(yán)密系統(tǒng)設(shè)計(jì):方法、趨勢(shì)與挑戰(zhàn)
- Scientific Computing with Scala
- 深入淺出PostgreSQL
- ASP.NET Core 2 Fundamentals
- HTML5與CSS3基礎(chǔ)教程(第8版)
- NoSQL數(shù)據(jù)庫(kù)原理
- SSH框架企業(yè)級(jí)應(yīng)用實(shí)戰(zhàn)
- OpenCV 3.0 Computer Vision with Java