條款01:視C++爲一個語言聯邦
C++是由下述四個次語言組成:
- C。C++支持面向過程編程,所用基礎語法完全等同於C。
- Object-Orited C++。C++面向對象編程,級C with Classed。
- Template C++。C++泛型編程(generic programming)部分。
- STL。STL是C++的一個template程序庫,其中包含了大量的容器、迭代器、算法。
因此,C++高效編程守則視狀態而變化,取決於使用C++的哪一個次語言。
條款02:儘量以const, enum, inline替換#define
-
#define是預處理器命令,對編譯器不可見。因此#define定義的符號,編譯器是不可見的,當存在編譯報錯時,難以排查。使用常量表達式替換宏是一個比較好的做法,如下:
#define ASPECT_RATION 1.653 --> const double AspectRation = 1.653;
-
#define沒有作用域的概念,一旦宏被定義,他就在其後的編譯過程中有效(除非觸發#undef)。
-
使用#define定義的宏來當做函數,由於括號問題容易引入異常。可使用template inline函數替代。如下:
#define CALL_WITH_MA(a, b) f((a) > (b) ? (a) : (b)) // CALL_WITH_MAX(++a, b),a被累加二次,當 a+1 > b時。 --> template<typename T> inline void callWithMax(const T& a, const T&b) { f(a > b ? a :b); }
條款03:儘可能使用const
const允許指定一個語義約束,從而獲得編譯器的幫助,確保約束不會被違反。
1、const修飾指針
char greeting[] = "hello";
const char *p = greeting; // const出現在\*號左邊,表示被指物是常量
char* const p = greeting; // 出現在\*號右邊,表示指針自身是常量。
2、const修飾迭代器
std::ventor<int> vec;
const std::vector<int>::iterator iter = vec.begin(); // iter是個常量,不能指向其它迭代器
std::ventor<int>::const_iterator cIter = vec.begin(); // cIter指向的對象時常量,不能被更改
3、const成員函數
const成員函數是指只能讀取成員變量,而不能改變成員變量(static除外)。兩個成員函數如果只是常量性(constness)不同,可以被重載。
-
bitwise constness(physical constness):不更改對象內任何一個bit
class CTextBlock { public: ... char & operator[] (std::size_t position) const { // bitwise聲明,但其實不適當,原因是返回值可以被修改。 return pText[position]; } private: char *pText; };
-
logical constness:const成員函數可以修改對象內某些成員變量,但客戶端偵測不出來。
使用 mutable 關鍵字修飾的成員變量,表示總能可以被更改,即使在const成員函數內。
-
在const和non-const成員函數中避免重複
考慮到兩個成員函數的功能幾乎一樣,唯一不同在於一個返回const,另一個返回non const,所以實現兩個這樣的函數,容易造成代碼重複。
一種可用的方法是,將重複代碼放到一個新的函數調用,但這種方式還是存在問題:函數調用、兩次return等。另一種方式是另其中一個調用另一個。class TextBlock { public: const char& operator[] (std::size_t position) const { ... ... ... return text[position]; } char& operator[] (std::size_t position) { return const_cast<char&>( static_cast<const TextBlock&>(*this)[position]); } ... };
條款04:確定對象被使用前已被初始化
C++規定,對象的成員變量初始化動作發生在進入構造函數本體之前,所以對於構造函數,最好實現成員初始化列表。值得注意的是,class的成員變量總是以其聲明次序被初始化。