一、讓自己習(xí)慣C++

第一章介紹 C++ 的一些基本方式。

1. 視 C++ 為一個(gè)語(yǔ)言聯(lián)邦

現(xiàn)在 C++ 已經(jīng)是個(gè)多重泛型編程語(yǔ)言,同時(shí)支持過(guò)程、面向?qū)ο蟆⒑瘮?shù)、泛型、元編程形式。將其視為語(yǔ)言聯(lián)邦,主要有四個(gè)次語(yǔ)言:

  • C
    區(qū)塊、語(yǔ)句、預(yù)處理、內(nèi)置數(shù)據(jù)類型、數(shù)組、指針都來(lái)自C。此時(shí)傳值比傳引用高效。
  • Object-Oriented C++
    類(構(gòu)造、析構(gòu)函數(shù))、封裝、繼承、多態(tài)、虛函數(shù)。傳引用比傳值高效。
    -Template C++
    新的編程范型:模板元編程
  • STL
    容器、迭代器、算法、函數(shù)對(duì)象。傳值更高效。

總結(jié):

C++ 高效編程守則視狀況而變化,取決于使用 C++ 的哪一部分。

2. 盡量以const, enum, inline 替換 #define

寧可以編譯器替換預(yù)處理器。#define不被視為語(yǔ)言的一部分,所以編譯出錯(cuò)時(shí)難以查找。如:

#define ASPECT_RATIO 1.653
應(yīng)換為
const double AspectRatio = 1.653

此時(shí)AspectRatio會(huì)進(jìn)入編譯器的記號(hào)表,而且對(duì)于浮點(diǎn)數(shù)使用常量可能比 #define 碼量更小。常量替換 #define 的兩種特殊情況:

  1. 定義常量指針

    例如定義一個(gè)常量的char *-based字符串:
    改為 string 更合適:
  2. class專屬常量

    為了將常量的作用域限制在class中,需將其設(shè)為class的一個(gè)成員;要確保其只有一份實(shí)體,需聲明為static成員。
    另外需要提供定義式才能取地址:

    一般形式為:
    但是當(dāng)這個(gè)常量用于指定數(shù)組大小,可改用“the enum hack”: 一個(gè)屬于枚舉類型的數(shù)值可權(quán)充 ints 被使用。
    enum比較像#define, 取enum 地址不合法,但是取const地址合法。enums 與 #defines一樣絕不會(huì)導(dǎo)致非必要的內(nèi)存分配。

另一個(gè)#define的誤用是用它實(shí)現(xiàn)宏。如

首先寫起來(lái)麻煩,而且可能導(dǎo)致不可思議的問(wèn)題:
改為:

產(chǎn)生一整群函數(shù),每個(gè)函數(shù)接受兩個(gè)同型對(duì)象。

總結(jié)

  • 對(duì)于單純常量,最好以const對(duì)象或enums替換#define
  • 對(duì)于形如函數(shù)的宏,最好改用inline函數(shù)替換#define

3. 盡可能使用const

關(guān)鍵詞const 用于指定一個(gè)不該被改動(dòng)的對(duì)象,加上這個(gè)約束后編譯器會(huì)強(qiáng)制保證實(shí)施。所以只要某個(gè)值確定應(yīng)該不變,就應(yīng)該加上以得到編譯器的幫助。

當(dāng)const和指針組合時(shí):

  • const出現(xiàn)在星號(hào)左邊——被指物是常量
  • const出現(xiàn)在星號(hào)右邊——指針自身是常量
  • 兩邊都有——被指物和指針都是常量

以相對(duì)指針的位置來(lái)判斷,所以以下兩種寫法相同:


當(dāng)const和STL迭代器組合時(shí):迭代器的作用就像是 T* 指針。const 迭代器 = T* const 指針,表示這個(gè)迭代器不得指向不同的東西,但所指對(duì)象的值可變。如:
符號(hào)看起來(lái)有點(diǎn)易混淆。

當(dāng)const和函數(shù)聲明組合時(shí):返回一個(gè)常量值

可以防止如下暴行:
顯然對(duì)于內(nèi)置類型來(lái)說(shuō)式子不成立,良好的用戶自定義類型也應(yīng)該避免無(wú)端的與內(nèi)置類型不兼容,所以返回const常量可阻止這類錯(cuò)誤。

const成員函數(shù)

const用于成員函數(shù)是為了保證該函數(shù)可作用于const對(duì)象。這類函數(shù)重要的理由:

  1. 使class接口比較容易被理解
  2. 使操作const對(duì)象成為可能

兩個(gè)函數(shù)如果只是常量性不同,可以重載。

程序中const對(duì)象大多用于傳指針或傳引用,故以下的調(diào)用方式更常見(jiàn):
調(diào)用方式

錯(cuò)誤在于企圖對(duì)由const版的operator[]返回的const char &施行賦值。

const成員函數(shù)的兩種概念:

  1. bitwise constness認(rèn)為成員函數(shù)只有在不更改對(duì)象的任何成員變量時(shí)才能稱為const。所以const成員函數(shù)不能更改對(duì)象內(nèi)任何non-static成員變量。
    但一個(gè)更改了指針?biāo)肝锏某蓡T函數(shù)雖然不算const,若只有指針屬于對(duì)象,那么不會(huì)引發(fā)編譯錯(cuò)誤。

最終改變了常量對(duì)象的值。

  1. logical constness
    由于存在以上的錯(cuò)誤,這里主張一個(gè)const成員函數(shù)可以修改它所處理的對(duì)象內(nèi)的某些bits。如:
    為了可以修改這兩個(gè)數(shù)據(jù),可用mutable釋放non-static成員變量的bitwise constness約束。

在const和non-const成員函數(shù)中避免重復(fù)

有時(shí)候成員函數(shù)需要進(jìn)行多個(gè)步驟,這就造成const和non-const成員函數(shù)存在大量重復(fù):

解決辦法是常量性轉(zhuǎn)除,即用non-const調(diào)用const函數(shù)。
有兩個(gè)轉(zhuǎn)型動(dòng)作,若non-const函數(shù)單純調(diào)用operator[]會(huì)遞歸調(diào)用自己。所以第一次是 *this轉(zhuǎn)為const,第二次是從const operator[]的返回值中移除const。使用static-cast做安全轉(zhuǎn)型,用const-cast移除const。

值得注意的是反向調(diào)用是錯(cuò)誤的。用const調(diào)用non-const冒著對(duì)象被改的風(fēng)險(xiǎn)。

總結(jié)

  • 將某些東西聲明為const可幫助編譯器偵測(cè)出錯(cuò)誤用法。const可被施加于任何作用域內(nèi)的對(duì)象、函數(shù)參數(shù)、函數(shù)返回類型、成員函數(shù)本體。
  • 編譯器強(qiáng)制實(shí)施bitwise constness,但編寫程序時(shí)應(yīng)使用概念上的常量性。
  • 當(dāng)const和non-const成員函數(shù)有著實(shí)質(zhì)等價(jià)的實(shí)現(xiàn)時(shí),令non-const版本調(diào)用const版本可避免代碼重復(fù)。

4. 確定對(duì)象使用前已先被初始化

C++ 變量在聲名時(shí)有時(shí)會(huì)被初始化有時(shí)不會(huì),需手工完成。

至于內(nèi)置類型外的東西,要確保每一個(gè)構(gòu)造函數(shù)都將對(duì)象的每一個(gè)成員初始化。但不要混淆賦值與初始化:
而C++規(guī)定:對(duì)象的成員變量的初始化操作發(fā)生在進(jìn)入構(gòu)造函數(shù)本體之前。較好的寫法是用成員初值列:

此時(shí)省去了default構(gòu)造的過(guò)程,都進(jìn)行copy構(gòu)造。也可在列用default構(gòu)造:

而且如果成員變量是const或reference就一定需要初值,不能被賦值。所以最簡(jiǎn)單的做法就是:總是使用成員初值列。

C++ 也有固定的成員初始化次序:基類早于子類,類的成員變量總是以其聲明次序被初始化。

non-local static對(duì)象的初始化次序

所謂static對(duì)象,壽命從被構(gòu)造出來(lái)到程序結(jié)束為止。也包括global對(duì)象,其中函數(shù)內(nèi)的static對(duì)象為local的。它們的析構(gòu)函數(shù)會(huì)在main函數(shù)結(jié)束時(shí)自動(dòng)調(diào)用。

現(xiàn)在客戶建立如下類處理文件目錄:


此時(shí)就必須保證tfs在tempDir之前被初始化。正確的做法是 將每個(gè)non-local static對(duì)象搬到自己的專屬函數(shù)內(nèi),即用local static對(duì)象替換non-local static對(duì)象。這么做的基礎(chǔ)是:C++ 保證函數(shù)內(nèi)static對(duì)象會(huì)在該函數(shù)被調(diào)用期間首次遇上該對(duì)象的定義式時(shí)被初始化。如:

現(xiàn)在使用函數(shù)返回的指向static對(duì)象的reference,而不再使用static對(duì)象自身。
所以為了避免初始化之前使用對(duì)象需要做:

  1. 手工初始化內(nèi)置型non-member對(duì)象
  2. 使用成員初值列對(duì)付對(duì)象的所有成分
  3. 在初始化次序不確定時(shí)用函數(shù)返回的指向static對(duì)象的reference。

總結(jié)

  1. 為內(nèi)置型對(duì)象進(jìn)行手工初始化,C++不保證初始化。
  2. 構(gòu)造函數(shù)最好使用成員初值列,不要再構(gòu)造函數(shù)本體內(nèi)用賦值操作。初值列列出的變量次序應(yīng)與聲明次序同
  3. 為免除“跨編譯單元的初始化次序”問(wèn)題,用local static對(duì)象替換non-local static對(duì)象。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. 讓自己習(xí)慣C++ 條款01:視C++為一個(gè)語(yǔ)言聯(lián)邦 為了更好的理解C++,我們將C++分解為四個(gè)主要次語(yǔ)言:...
    Mr希靈閱讀 2,981評(píng)論 0 13
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,655評(píng)論 1 51
  • 1 讓自己習(xí)慣 C++ 條款01:視 C++ 為一個(gè)語(yǔ)言聯(lián)邦 將C++視為一個(gè)由相關(guān)語(yǔ)言組成的聯(lián)邦而非單一語(yǔ)言。在...
    暗夜望月閱讀 455評(píng)論 0 1
  • C++文件 例:從文件income. in中讀入收入直到文件結(jié)束,并將收入和稅金輸出到文件tax. out。 檢查...
    SeanC52111閱讀 3,090評(píng)論 0 3
  • 現(xiàn)在開始上班了,很久沒(méi)有更新了.今天寫個(gè)collectionView給大家看看,這種樣式的collectionvi...
    徐老茂閱讀 939評(píng)論 3 12

友情鏈接更多精彩內(nèi)容