C++11——變量和基礎(chǔ)類型

列表初始化(List Initialization)

當(dāng)初始化列表與內(nèi)置類型的變量一起使用時(shí)汰瘫,這種初始化形式具有一個(gè)重要屬性:如果初始化器可能導(dǎo)致信息丟失狂打,編譯器將不允許我們列出內(nèi)置類型的初始化變量。

Code:
    long double ld = 3.1415926536;
    int a{ld}, b = {ld};    // error:narrowing conversion required
    int c(ld), d = ld;      // ok: but value will be truncated

編譯器拒絕初始化ab混弥,因?yàn)槭褂?code>long double初始化int可能會(huì)丟失數(shù)據(jù)趴乡。至少ld的小數(shù)部分將被截?cái)啵硗?code>ld中的整數(shù)部分可能太大而不適合int蝗拿。
這一新特性似乎與C++98區(qū)別不大晾捏,畢竟我們不太可能直接使用long double初始化int。然而在“模版編程和范型編程”中哀托,可能無意中發(fā)生這樣的初始化惦辛!

空指針(Null Pointers)

初始化指針最直接的方法是使用新標(biāo)準(zhǔn)引入的字面量nullptr初始化指針。 nullptr是一個(gè)具有特殊類型的字面量仓手,可以轉(zhuǎn)換為任何其他指針類型胖齐。 或者我們可以使用字面量0初始化一個(gè)指針。
較老的程序有時(shí)使用預(yù)處理程序變量NULL初始化指針嗽冒。該變量在cstdlib頭文件中被定義為0呀伙。
現(xiàn)代C++程序通常應(yīng)該避免使用NULL,而應(yīng)該使用nullptr作為替代添坊。

Code:
    int *p1 = nullptr;    // equivalent to int *p1 = 0;
    int *p2 = 0;          // directly initializes p2 from the literal constant 0
    // #include <cstdlib>
    int *p3 = NULL;       // equivalent to int *p3 = 0;

int型變量賦給指針是非法的剿另,即使該int變量的值恰好為0。

Code:
    int zero = 0;
    int *pi = zero;    // error: cannot assign an int to a pointer

初始化指針的注意事項(xiàng)

未初始化的指針是運(yùn)行時(shí)錯(cuò)誤(run-time errors)的常見原因贬蛙。與任何其他未初始化的變量一樣雨女,當(dāng)我們使用未初始化的指針時(shí)會(huì)發(fā)生什么是未定義的。 使用未初始化的指針幾乎總會(huì)導(dǎo)致運(yùn)行時(shí)崩潰阳准。然而調(diào)試這種崩潰往往非常困難氛堕。
我們建議初始化所有變量特別是指針。 如果可能僅在定義了欲指向的對(duì)象之后定義指針野蝇。如果指針沒有要指向的對(duì)象岔擂,那么就將指針初始化為nullptr或0位喂,這樣程序就可以判斷這個(gè)指針沒有指向任何對(duì)象浪耘。


constexpr變量(constexpr Variables)

常量表達(dá)式是一個(gè)表達(dá)式乱灵,其值不能更改,并且可以在編譯時(shí)進(jìn)行計(jì)算七冲。

Code:
    const int max_files = 20;          // max_files is a constant expression
    const int limit = max_files + 1;   // limit is a constant expression
    const int sz = get_size();         // sz is not a constant expression

雖然sz是一個(gè)const int類型的變量痛倚,但它不是一個(gè)常量表達(dá)式,因?yàn)樗某跏蓟鞯闹抵挥性谶\(yùn)行時(shí)才能獲得澜躺。
在新標(biāo)準(zhǔn)下蝉稳,我們可以通過使用constexpr聲明一個(gè)變量,從而要求編譯器去驗(yàn)證該變量是否為常量表達(dá)式掘鄙。

Code:
    constexpr int mf = 20;          // 20 is a constant expression
    constexpr int limit = mf + 1;   // mf + 1 is a constant expression
    constexpr int sz = size();      // ok only if size is a constexpr function

雖然我們不能使用普通函數(shù)作為constexpr變量的初始化函數(shù)耘戚,但新標(biāo)準(zhǔn)允許我們將某些函數(shù)定義為constexpr。 這些函數(shù)必須足夠簡單操漠,以便編譯器可以在編譯時(shí)對(duì)它們進(jìn)行評(píng)估收津。 我們可以在constexpr變量的初始化器中使用constexpr函數(shù)。
通常浊伙,最好將constexpr用于打算用作常量表達(dá)式的變量撞秋。

類型別名(Type Aliases)

通常我們使用typedef來定義類型別名,新標(biāo)準(zhǔn)引入了另一種通過別名聲明來定義類型別名的方法:

Code:
    using SI = Sales_item;    // SI is a synonym for class Sales_item

別名聲明以關(guān)鍵字using開頭嚣鄙,后跟別名和=吻贿。 別名聲明將=左側(cè)的名稱定義為右側(cè)顯示的類型的別名。類型別名是類型名稱哑子,可以出現(xiàn)在類型名稱出現(xiàn)的任何位置:

Code:
    SI item;                // same as Sales_item item

auto類型說明符(The auto Type Specifier)

auto類型說明符是C++11全新引入的舅列。
編寫程序過程中我們想要將表達(dá)式的值存儲(chǔ)在變量中并不罕見。 要聲明變量卧蜓,我們必須知道該表達(dá)式的類型帐要。 當(dāng)我們編寫程序時(shí),確定表達(dá)式的類型可能會(huì)非常困難烦却,有時(shí)甚至是不可能的宠叼。 在新標(biāo)準(zhǔn)下,通過使用auto類型說明符我們可以讓編譯器為我們找出類型其爵。 與類型說明符(例如double)命名特定類型不同冒冬,auto告訴編譯器要從初始化程序中推斷出類型。 因?yàn)榫幾g器對(duì)于auto說明符需要推斷的依據(jù)摩渺,所以使用auto作為其類型說明符的變量必須具有初始化器:

Code:
    // the type of item is deduced from the type of the result of adding val1 and val2
    auto item = val1 + val2; // item initialized to the result of val1 + val2

此處編譯器將根據(jù)val1 + val2返回的類型來推斷item的類型简烤。如果val1val2是類Sales_item的對(duì)象,那么item將具有類型Sales_item摇幻。如果val1val2的類型是double横侦,那么item將是類型double的變量挥萌。
與任何其他類型說明符一樣,我們可以使用auto定義多個(gè)變量枉侧。 因?yàn)槁暶髦簧婕皢蝹€(gè)基類型引瀑,所以聲明中所有變量的初始化器必須具有彼此一致的類型:

Code:
    auto i = 0, *p = &i;       // ok: i is int and p is a pointer to int
    auto sz = 0, pi = 3.14;    // error: inconsistent types for sz and pi
符合類型,const和auto

編譯器推斷auto的類型并不總是與初始化程序的類型完全相同榨馁。 相反憨栽,編譯器會(huì)調(diào)整類型以符合常規(guī)初始化規(guī)則。
首先翼虫,當(dāng)我們使用引用時(shí)屑柔,我們實(shí)際上正在使用被引用的對(duì)象的一個(gè)引用。 特別是當(dāng)我們使用引用作為初始化器時(shí)珍剑,初始化器是相應(yīng)的對(duì)象掸宛。 編譯器將該對(duì)象的類型用于auto的類型推導(dǎo):

Code:
    int i = 0, &r = i;
    auto a = r;  // a is an int (r is an alias for i, which has type int)

其次,auto通常會(huì)忽略頂層const招拙。 但初始化時(shí)會(huì)保持低層const唧瘾,例如初始化器是指向const的指針時(shí):

Code:
    const int ci = i, &cr = ci;
    auto b = ci;  // b is an int (top-level const in ci is dropped)
    auto c = cr;  // c is an int (cr is an alias for ci whose const is top-level)
    auto d = &i;  // d is an int*(& of an int object is int*)
    auto e = &ci; // e is const int*(& of a const object is low-level const)

如果我們想要推理出保留頂層const的類型,需要進(jìn)行顯示聲明:

Code:
    const auto f = ci; // deduced type of ci is int; f has type const int

我們還可以指定對(duì)auto推理出的類型的引用迫像。 通常的初始化規(guī)則仍然適用:

Code:
    auto &g = ci;         // g is a const int& that is bound to ci (can't ignore the top-level const in ci)
    auto &h = 42;         // error: we can't bind a plain reference to a literal
    const auto &j = 42;   // ok: we can bind a const reference to a literal

當(dāng)我們要求引用由auto推理出的類型時(shí)劈愚,初始化器中的頂層const不會(huì)被忽略。 通常當(dāng)我們將引用綁定到初始化器時(shí)闻妓,const就不是頂層的了菌羽。
當(dāng)我們?cè)谕粋€(gè)語句中定義多個(gè)變量時(shí),重要的是要記住引用或指針是特定聲明符的一部分由缆,而不是聲明的基本類型的一部分注祖。 通常初始化器必須提供一致的自動(dòng)推導(dǎo)類型:(注:auto用于推導(dǎo)基本類型)

Code:
    auto k = ci, &l = i;      // k is int; l is int&
    auto &m = ci, *p = &ci;   // m is a const int&;p is a pointer to const int
    // error: type deduced from i is int; type deduced from &ci is const int
    auto &n = i, *p2 = &ci;

注:\color{red}{auto容易被濫用!}使用過程中應(yīng)該時(shí)刻注意由auto推理出的類型均唉!

decltype類型說明符(The decltype Type Specifier)

有時(shí)我們想要定義一個(gè)變量是晨,該變量具有編譯器從表達(dá)式推導(dǎo)出的類型,但不希望使用該表達(dá)式來初始化變量舔箭。 對(duì)于這種情況罩缴,新標(biāo)準(zhǔn)引入了第二個(gè)類型說明符decltype,它返回其操作數(shù)的類型层扶。 編譯器分析表達(dá)式以確定其類型箫章,但不評(píng)估表達(dá)式:

Code:
    decltype(f()) sum = x;   // sum has whatever type f returns

這里,編譯器不調(diào)用函數(shù)f()镜会,但它使用函數(shù)f()返回的類型作為sum的類型檬寂。
decltype處理頂層const和引用的方式與auto的處理方式略有不同。 當(dāng)我們對(duì)一個(gè)變量應(yīng)用decltype時(shí)戳表,decltype返回該變量的類型桶至,包括頂層const和引用:

Code:
    const int ci = 0, &cj = ci;
    decltype(ci) x = 0;     // x has type const int
    decltype(cj) y = x;     // y has type const int& and is bound to x
    decltype(cj) z;         // error: z is a reference and must be initialized

值得注意的是昼伴,decltype是唯一一個(gè)定義為引用的變量不被視為它所引用的對(duì)象的同義詞的說明符。

decltype和引用

當(dāng)我們將decltype應(yīng)用于不是變量的表達(dá)式時(shí)镣屹,我們得到該表達(dá)式返回的類型圃郊。某些表達(dá)式將導(dǎo)致decltype生成引用類型, 通常decltype為該類表達(dá)式返回一個(gè)引用類型野瘦,這些表達(dá)式產(chǎn)生的對(duì)象可以作為左值:

Code:
    // decltype of an expression can be a reference type
    int i = 42, *p = &i, &r = i;
    decltype(r + 0) b;    // ok: addition yields an int; b is an (uninitialized) int
    decltype(*p) c;       // error: c is int& and must be initialized

此處r是一個(gè)引用描沟,所以decltype(r)是一個(gè)引用類型。如果我們想要獲得r引用的類型鞭光,我們可以將r應(yīng)用到表達(dá)式中(如:r + 0),這個(gè)表達(dá)式將產(chǎn)生一個(gè)不是引用類型的值泞遗。
另一方面惰许,解除引用運(yùn)算符是decltype返回引用的表達(dá)式的一個(gè)例子。 正如我們所見史辙,當(dāng)我們解引用指針時(shí)汹买,我們得到指針指向的對(duì)象。 而且聊倔,我們可以對(duì)該對(duì)象進(jìn)行賦值晦毙。 因此,由decltype(*p)推導(dǎo)出的類型是int&耙蔑,而不是無格式的int见妒。
decltypeauto之間的另一個(gè)重要區(qū)別是decltype所做的推論取決于其給定表達(dá)式的形式。 令人困惑的是將變量名用括號(hào)括起來將會(huì)影響decltype返回的類型甸陌。 當(dāng)我們將decltype應(yīng)用于沒有任何括號(hào)的變量時(shí)须揣,我們得到該變量的類型。 如果我們將變量的名稱包裝在一組或多組括號(hào)中钱豁,編譯器會(huì)將變量及括號(hào)計(jì)算為一個(gè)表達(dá)式耻卡。 變量是一個(gè)可以作為左值進(jìn)行賦值的表達(dá)式。結(jié)果是decltype作用于這樣的表達(dá)式上將產(chǎn)生一個(gè)引用:

Code:
    // decltype of a parenthesized variable is always a reference
    decltype((i)) d;    // error: d is int& and must be initialized
    decltype(i) e;      // ok: e is an (uninitialized) int

\color{red}{注:}decltype((variable))\color{red}{一直是一個(gè)引用類型牲尺;但是}decltype(variable)\color{red}{僅當(dāng)}variable\color{red}{是一個(gè)引用類型時(shí)才會(huì)返回引用卵酪!}

參考文獻(xiàn)

[1] Lippman S B , Josée Lajoie, Moo B E . C++ Primer (5th Edition)[J]. 2013.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市谤碳,隨后出現(xiàn)的幾起案子溃卡,更是在濱河造成了極大的恐慌,老刑警劉巖估蹄,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件塑煎,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡臭蚁,警方通過查閱死者的電腦和手機(jī)最铁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門讯赏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人冷尉,你說我怎么就攤上這事漱挎。” “怎么了雀哨?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵磕谅,是天一觀的道長。 經(jīng)常有香客問我雾棺,道長膊夹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任捌浩,我火速辦了婚禮放刨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘尸饺。我一直安慰自己进统,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布浪听。 她就那樣靜靜地躺著螟碎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪迹栓。 梳的紋絲不亂的頭發(fā)上掉分,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音迈螟,去河邊找鬼叉抡。 笑死,一個(gè)胖子當(dāng)著我的面吹牛答毫,可吹牛的內(nèi)容都是我干的褥民。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼洗搂,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼消返!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起耘拇,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤撵颊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后惫叛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倡勇,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年嘉涌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了妻熊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夸浅。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖扔役,靈堂內(nèi)的尸體忽然破棺而出帆喇,到底是詐尸還是另有隱情,我是刑警寧澤亿胸,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布坯钦,位于F島的核電站,受9級(jí)特大地震影響侈玄,放射性物質(zhì)發(fā)生泄漏婉刀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一拗馒、第九天 我趴在偏房一處隱蔽的房頂上張望路星。 院中可真熱鬧,春花似錦诱桂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至堤尾,卻和暖如春肝劲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背郭宝。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來泰國打工辞槐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人粘室。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓榄檬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親衔统。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鹿榜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容