關於EffectIve C++ 的總結(侯捷翻譯版)
C++ 提供四種不同而有相輔相成的程式設計範型(programming paradigms) 如下:
(1)procedural based
(2)object based
(3)object oriented
(4)generics
item1: 將C++視為一個語言聯邦
這個語言聯邦有四個“州”(即C++包含這四個部分):
(1)C, C++ 起源於C, 一開始只是在C的基礎上加上了一些物件導向的特性。 C++ 的最初名稱為C with classes。 發展到今天, C 語言沒有的特性如下: 沒有模板(template), 沒有異常(exception), 沒有過載(overloading)等等。
(2)object-oriented C++。 這是C with class所訴求的。 classes, 封裝(encapsulation), 繼承(Inheritance), 多型(Polymorphism), Virtual 函式(動態繫結)等等。 這是物件導向的。
(3)Template C++. 這是C++ 泛型程式設計的部分。 模板很強大。
(4)STL. STL 是一個template程式庫。 具有容器, 迭代器, 演算法, 函式物件。
Item 2: 儘量使用const, enum, inline, 用於替換由#define的macro(巨集)
這個條款的意思是“寧可以編譯器替換前處理器”。 因為#define 不被視為語言的一部分。 例如:
#define ASPECT_RATIO 1.653
記號 ASPECT_RATIO 並不被我們的Compiler看見。 編譯器在處理我們的source code的之前, 記號ASPECT_RATIO 就被前處理器移走了。 ASPECT_RATIO 就沒有進入Compiler 用於記錄源程式的記號表中(symbol table). 所以出現錯誤的時候。 錯誤資訊會提到1.653而不是ASPECT_RATIO。 如果ASPECT_RATIO被定義在一個非你所寫的標頭檔案中, 我們可能對於1.653 毫無概念。 除錯起來也很費時間。
解決之道是用常量替換上述的巨集(#deine):
const double AspectRation = 1.653; // 大寫名稱常用於巨集, 所以這裡改變名稱寫法
作為語言常量, AspectRatio 肯定會被編譯器看到。 當然也會進入幾號表內。 另外, 如果使用const (常量)代替#define 巨集的另外一個好處是導致記憶體小。 因為前處理器會盲目的將ASPECT_RATIO 替換為1.653, 這樣我們的object code 就會有多份1.653。 但是對於常量Aspect_Ratio , 這是絕對不會發生的。 因為如果我們的含有常量Aspect_Ratio 定義的標頭檔案被多個source file 包含的時候, 我們的編譯器會產生對這個常量重複定義的錯誤,
即a linker error from multiple definitions of the symbol。 但是#define的標頭檔案被多個原始檔包含是, 編譯卻不會發生錯誤。 通常巨集#define 的記憶體為: 巨集的大小 x 包含含有巨集的標頭檔案的原始檔個數。
建立class的專屬常量。 也就是說將常量的作用域(scope)限制於class內, 你就必須讓常量稱為class的一個member。 為了確保常量至多隻有一份實體, 必須將其宣告為static 成員。
例如:
class GamePlayer {
private:
static const int NumTurns = 5; // 常量宣告式
int scores[NumTurns]; // 使用該常量
};
注意, 上面的NumTurns 是宣告式, 而非定義常量的式子。
通常C++ 要求你對你所使用的任何東西提供一個定義式。 但是如果它是一個class的專屬常量, 又是static,並且為整形型別(Integral type, 例如int, char, bool), 則需要特別處理。 只要不取用它的地址, 我們可以宣告而無需提供定義式。 但是如果你取某個class的專屬常量的地址, 或者總是你不取, 但是你的編譯器卻(不正確的)堅持要看到一個定義式, 此時, 你就必須另外提供定義式如下:
const int GamePlayer::NumTurns; //NumTurns 的定義, 下面告訴你為什麼沒有給與數值
上述的這個式子應該放在實現檔案中, 而非標頭檔案中。 由於class的常量已經在宣告的時候獲得初值(為5), 所以定義的時候不可以再設初值。
我們無法使用#define 去建立一個class的專屬常量。 因為一巨集被定義, 它在其後的編譯過程中有效(除非在某處被# undef)。 這意味著巨集無法提供封裝性。
舊式編譯器一般不支援上述的語法。 如果你的編譯器不支援, 你可以將初值放在定義式中:
class CostEstimate {
private:
static const double FudgeFactor; // 常量宣告式
...
}; // 位於標頭檔案中
const double CostEstimate::FudgeFactor = 1.35; // 放置在實現檔案中
這幾乎是你在任何時候唯一需要做的事。 唯一的例外是, 當你的class在編譯期間需要一個class的常量值。
例如, 在上述的GamePlayer::scores的陣列宣告式中(編譯器堅持在編譯期間知道陣列的大小(除非動態分配的動態陣列))。 這時候萬一你的編譯器(錯誤的)不允許“static const” 常量無法完成“in class 初值設定”, 那麼可以改用the enum hack 的補償做法。
這樣做的理論基礎是: 一個屬於列舉型別的數值可充當ints 被使用。 這樣定義如下:
class GamePlayer {
private:
enum{NumTurns = 5};
int scores[NumTurns];
};
enum hack 的認識如下:
取一個const 的地址合法, 但是取一個enum 的地址是不合法的, 而去一個#define 的地址也是不合法的。
現在看看前處理器。
一個常見的#define的誤用情況是依他實現巨集(macros)。
巨集看起來像函式, 但是不會招致函式呼叫(function call )帶來的額外開銷。
巨集有很多的缺點, 想想就讓人痛苦。
無論何時, 定義巨集的時候, 需要在巨集中將所有的實參獎賞小括號。 否則別人呼叫起來就麻煩了。 幸運的是, 我們可以寫出template inline 函式, 一便獲得巨集的效率的同時, 由能夠避免巨集的缺點:
template <class T>
inline void callWithMax(const T&a, const T& b) {
f(a>b ? a:b);
}
這樣決定較大者呼叫函式f。
有了const, enum, inline, 我們對前處理器的需求降低了。 但是#include 仍然必須, 而#ifndef等也繼續扮演著控制編譯的重要的角色。
但是請記住:
(1)對於單純的常量, 最好以const 物件或者enum 去替換 #define。
(2)對於形似函式的巨集(macro), 最好改用inline 函式替換 #defines。
相關文章
- 侯捷C++ STL體系結構與原始碼剖析:關於moveable的說明C++原始碼
- 侯捷老師C++學習路線C++
- OpenCV翻譯總結OpenCV
- 侯捷C++物件導向高階開發筆記C++物件筆記
- Effective Clustering on Large Attributed Bipartite Graphs翻譯
- 《Effective C++》閱讀總結(三):資源管理C++
- OpenCV翻譯專案總結二——Mat翻譯OpenCV
- OpenCV Core functionality翻譯總結OpenCVFunction
- 侯捷C++手把手教學:堆、棧與記憶體管理C++記憶體
- 《Effective C++》閱讀總結(四): 設計、宣告與實現C++
- OpenCV翻譯及專案總結一OpenCV
- 關於Electron原生模組編譯的一點總結編譯
- 《Effective C++》閱讀總結(二):類的構造、析構和賦值C++賦值
- 關於近期的總結
- 請豆包幫忙總結翻譯一個網站,翻譯效果還不錯網站
- 關於describe和test執行順序的翻譯
- Effective C++ 條款08_不止於此C++
- mysql關於variable的總結MySql
- sqlserver關於always on的總結SQLServer
- 【譯】資料結構中關於樹的一切(java版)資料結構Java
- 關於翻譯包的擴充套件 dimsav/Laravel-translatable套件Laravel
- OfficialKaldi(一)| 關於Kaldi專案(翻譯註解)
- effective C++ : CHAPTER 8C++APT
- 【Effective Modern C++】索引C++索引
- Effective C++筆記C++筆記
- sqlserver 關於DBCC CHECKDB的總結SQLServer
- sqlserver關於mirror映象的總結SQLServer
- 關於golang的time包總結Golang
- 關於Servlet小總結Servlet
- 官方翻譯 | 有關基於文件的iOS應用開發iOS
- 【翻譯】AwesomeAsyncio中文版
- 【翻譯】c++類中“空成員”的優化C++優化
- 《Effective C++》第三版-5. 實現(Implementations)C++
- mysql關於mysqld_safe的總結MySql
- mysql關於mysql.server的總結MySqlServer
- 關於PaaS的純乾貨總結
- mysql關於表空間的總結MySql
- 關於Android Studio使用Git的總結AndroidGit
- 關於STL容器的簡單總結