《Effective C++》讀書筆記第一章:讓自己習慣c++

Accustoming Youself to C++

條款1.視c++爲一個語言聯邦

C++是個多重範型編程語言,同時支持過程形式、面向對象形式、函數形式、泛型形式、元編程形式。視c++爲一個語言聯邦,包括以下四個部分:

  1. C. C++說到底還是以C爲基礎。這是面向過程的部分,C與C++相比,少了模板、異常以及重載。
  2. Object-Oriented. 面向對象部分,包括封裝、繼承、多態以及虛函數綁定等。
  3. Template C++. 泛型編程部分,template metaprogramming(TMP,模板元編程),其中C++標準模板庫STL在這各部分的基礎上。
  4. STL. C++標準模板庫,包括容器、迭代器、算法以及函數對象等。

    請記住: 
    C++高效編程守則視狀況而變化,取決於使用的是這四個部分中的哪一部分。

條款2.儘量以const,enum,inline代替#define

等價描述爲寧可以編譯器代替預處理器。 
#define即宏定義在預處理階段進行處理,通常情況下記號名稱會進入記號表(symbol table),也有可能在編譯器處理前就被預處理器移走了,沒有進入記號表,這時就會編譯出錯。解決的辦法使用常量來代替宏。 
例子:const double AspecRatio = 1.653;來代替#define ASPEC_RATIO 1.653; 
特別注意兩點: 
- 指針常量與常量指針,特別地,使用const string代替const char* const; 
- class專屬常量:static const來修飾,靜態常量: 
例如:class GamePlayer{ static const int NumTurns = 5; //常量聲明式}; 
或者使用常量類內聲明+類外定義: 
class GamePlayer{static const int NumTurns;};// 常量聲明式 
const int GamePlayer::NumTurns = 5;//常量定義式
 
另外無法使用宏定義來創建一個class 專屬常量,因爲宏定義並不重視作用域。宏定義只是簡單嵌入,並不做類型檢查,所以在定義宏時,請爲宏的所有實參加上小括號。 
例如:#define CALL_MAX(a, b) ((a) > (b) ? (a) : (b)) 
調用:

int a = 5, b = 0;
CALL_MAX(++a, b);   //a被累加1次
CALL_MAX(++a, b + 10);  //a被累加2次
  • 1
  • 2
  • 3

這是不可預料的行爲,因此使用內聯函數來代替宏定義函數。 
請記住: 
對於單純常量,最好使用const對象或enum替換#defines。 
對於形似函數的宏,最好改用inline函數替換#defines。

條款3.儘可能使用const

const關鍵字告訴編譯器某值應該保持不變,即爲只讀的。 
常量指針與指針常量 
例如:STL中的迭代器。

const vector<int>::iterator ite1;//類似於常量指針,ite1是隻讀的
vector<int>::const_iterator ite2;//類似於指針常量,*ite2是隻讀的
  • 1
  • 2
  • const可以幫助編譯器甄別錯誤,例如判斷“==”和賦值。
const Rational operator*(const Rational& lhs, const Rational& rhs);//有理數類乘法重載
Rational a, b, c;
if(a * b = c)...//這一步本來是想做比較操作,誤寫成了賦值操作,因爲返回const對象,編譯器就能發現這個錯誤。
  • 1
  • 2
  • 3
  • const成員函數 
    const成員函數代表this指針的類型爲:const className const*。 
    改善C++程序效率的一個根本方法是以pass by reference-to-const方式傳遞對象,這一前提是必須使用const成員函數來處理const對象。 
    const成員函數的兩個流行概念: 
    bitwise constness(physical constness):任何成員變量都是隻讀時纔可以說是const。(在Object Model 2.2 Copy Constructor的構造操作中,bitwise copies) 
    logical constness:一個const成員函數可以修改它處理的對象內的某些bits,但只有在客戶端偵測不出來的情況下纔可以,例如修改指針所指的對象。 
    如何釋放non-static成員函數的bitwise constness約束:使用mutable關鍵字,即使是在const成員函數中,mutable修飾的成員也是可變的。 
    • 在const和non-cons成員函數中避免重複 
      利用non-const版本調用const版本,再加上轉型const_cast、static_cast,例如:
const char& operator[](int pos) const;//const版本
char& operator[](int pos) { //non-const版本
    return const_cast<char&> //返回值轉型,移除const
     (static_cast<const className&>(*this)[pos]);//*this轉型,增加const
}
  • 1
  • 2
  • 3
  • 4
  • 5

反過來使用const版本調用non-const版本則是一種錯誤行爲。 
請記住: 
將某些東西聲明爲const可以幫助編譯器偵測除錯誤;const可以被施加在任何作用域內的對象、函數參數、函數返回類型、成員函數本身。 
編譯器強制實施bitwise constness,但是你編寫程序時應該使用“概念上的常量性”(conceptual constness)。 
當const和non-const成員函數有着實質等價的實現時,使用non-const版本來調用const版本可以避免代碼重複。

條款4.確定對象使用前已被初始化

永遠在對象使用之前將其初始化,對於內置類型,請手動初始化;對於非內置類型,初始化的任務則落在了構造函數身上。 
別混淆賦值和初始化的概念,例如:

class ABEntry {
public:
    ABEntry(const string& name, const string& address, int num) {
        //這裏都是賦值,而非初始化,實際上是先調用default ctor,再賦值
        _name = name;
        _address = address;
        _num = num; 
        }
private:
    string _name;
    string _address;
    int _num;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

更好的辦法是使用所謂的成員初值列(member initialization list):

ABEntry::ABEntry(const string& name, const string& address, int num)
    : _name(name), _address(address), _num(num) { }
//直接進行copy ctor,就是初始化   
  • 1
  • 2
  • 3

特別注意的是:在初值列中列出所有的成員變量,避免遺漏;另外,如果成員變量是const 或者reference,一定需要初值,而不能賦值,最簡單的做法就是使用成員初值列。 
C++有着固定的初始化順序:按照聲明順序進行初始化,且基類先於派生類初始化

class init {
public:
        init(int i) : _j(i), _i(_j);
        //這裏會出現錯誤,因爲先對_i初始化,再對_j初始化。
private:
    int _i;
    int _j;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

對於local static object(局部靜態對象),程序結束之後,該對象會被自動銷燬。 
基於局部靜態對象的singleton設計模式:

class Singleton
{
public:
    static Singleton& getInstance() {
        static Singleton s;
        return s;
    }
private:
    Singleton() {}
    Singleton(const Singleton&);
    void operator=(const Singleton&);
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

請記住: 
爲內置類型進行手工初始化,C++不保證初始化它們。 
構造函數最好使用成員初值列,不要在構造函數中使用賦值操作,初值列中的成員變量的順序應該和聲明順序一致。 
爲免除“跨編譯單元之初始化次序”問題,使用返回local static對象引用的函數來代替non-local static對象。

參考自侯捷老師翻譯的《Effective C++》中文版第三版

轉載自:https://blog.csdn.net/qq_25467397/article/details/80344261

發佈了16 篇原創文章 · 獲贊 13 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章