Effective C++讀書筆記(一)

一、讓自己習慣C++


   條款01:視C++爲一個語言聯邦
   
C++視爲一個由相關語言組成的聯邦而非單一語言,由四個主要的次語言組成。

  • C。說到底C++仍是以C爲基礎。區塊,語句,預處理器,內置數據類型,數組,指針統統來自C。
  • Object-Oreinted C++。這一部分也就是C with Classes所訴求的。類,封裝,繼承,多態,virtual函數(動態綁定)。
  • Template C++。這是C++泛型編程部分。由於templates威力強大,它們帶來嶄新的編程範型,也就是所謂的template metaprogramming。
  • STL。STL是個template程序庫。它對容器,迭代器,算法以及函數對象的規約有極佳的緊密配合與協調。

 請記住:

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

條款02:儘量以constenuminline替換#define

  • const替換#define

#define  ASPECT_RATIO 1.653

這樣你的ASPECT_RATIO可能並未進入記號表,導致你對1.653以及它來自何處毫無概念,於是你將因爲追蹤它而浪費時間。

解決之道:const double  AspectRatio = 1.653;

AspectRatio肯定會被編譯器看到,當然就會進入記號表內。

常量替換#define兩點注意:

定義常量指針:

const char *authorName= “Shenzi”;
cosnt std::string authorName("Shenzi");

建議使用第二種。

類專屬常量:

爲了將常量的作用域限制於class內,你必須讓它成爲class的一個成員;而爲確保常量至多隻有一份實體,你必須讓它成爲一個static成員:

class GamePlayer

{

     private

          static const int NumTurns = 5//常量聲明式,如果不取它們的地址,你可以聲明並使用它們而無須提供定義式。

           int scores[NumTurns]//使用該常量

          ….

}

然而#define不能夠用來定義class專屬常量。

  • enum替換#define

萬一你編譯器不允許“static整數型class常量”完成“in class初值設定”,可改用所謂的“the enum hack”補償做法。

class GamePlayer

{

      private

          enum{NumTurns=5};

          intscores[NumTurns]

          ….

}

取一個const的地址是合法的,但取一個enum的地址就不合法,而取一個#define的地址通常也不合法。如果你不想讓別人獲取一個pointerreference指向你的某個整數常量,enum可以幫助你實現這個約束。

  • inline替換#define

宏有着太多的缺點,光是想到它們就讓人痛苦不堪。

#define CALL_WITH_MAX(a,b)    f((a) > (b)) ? (a) :(b))

替換:
template<typename T>

inline voidcallWithMax(cosnt T &a, cosnt T &b)

{
            f(a > b ? a : b);
}

 請記住:

  • 對於單純常量,最好以const對象或enums替換#defines
  • 對於形似函數的宏,最好改用inline函數替換#defines        

條款03:儘可能使用const條款

關鍵字const多才多藝:

面對指針:

char greeting[] = "Hello";
char *p = greeting;//non-const pointer,non-const data;
const char *p = greeting; //non-const pointer,const data;

char * const p = greeting; //constpointer,non-const data;
const char * const p = greeting;// const pointer, const data;

面對STL

const std::vector<int>::interator iter =vec.begin();//作用像T *const,++iter錯誤! iterconst
 std::vector<int>::const_iterator cIter = vec.begin();//
作用像const T**cIter = 10錯誤! *cIterconst

令函數返回一個常量值,往往可以降低因客戶錯誤而造成的意外,而不至於放棄安全性和高效性。

 例:const Rational operator*const Rational &lhs, cosnt Rational &rhs//防止出現  c= a*b

  • const成員函數

兩個成員函數如果只是常量性不同,可以被重載。

成員函數如果是const,這有兩個流行概念:bitwise constnesslogical constnessbitwise const陣營的人相信,成員函數只有在不更改對象之任何成員變量時纔可以說是const。也就是說它不更改對象內的任何一個bit。它對常量性的定義,因此const成員函數不可以更改對象內任何non-static成員變量。

不幸的是許多成員函數雖然不十足具備const性質卻能通過bitwise測試。更具體地說,一個更改了“指針所指物”的成員函數雖然不能算是const,但如果只有指針(而非其所指物)隸屬於對象,那麼此函數爲bitwise const不會引發編譯器異議。

class CTextBlock

{

     public:

           char &operator[](std::size_t position) const //bitwise const聲明,

           {returnpText[position];}                              //但其實不適當

};

這個class不適當地將其operator[]聲明爲const成員函數,而該函數卻返回一個reference指向對象內部值。

constCTextBlock cctb(“Hello”);

char *pc =&cctb[0];

*pt =’J’;//但你終究還是改變了它的值。

利用mutable釋放掉non-static成員變量的bitwise constness約束。

  • constnon-const成員函數中避免重複

cosntnon-const成員函數有着實質等價的實現時,令non-const版本調用const版本可避免代碼重複。

請記住:

  • 將某些東西聲明爲const可幫助編譯器偵測出錯誤用法。const可被施加於任何作用域內的對象、函數參數、函數返回類型、成員函數本體;
  • 編譯器強制實施bitwise constness,但你編寫程序時應該使用“概念上的車輛”(conceptual constness);
  • 當cosnt和non-const成員函數有着實質等價的實現時,令non-const版本調用const版本可避免代碼重複。

條款04:確定對象被使用前已先被初始化

永遠在使用對象之前先將它初始化。對於無任何成員的內置類型,你必須手工完成此事。至於內置類型以外的任何其它東西,初始化責任落在構造函數身上,確保每一個構造函數都將對象的每一個成員初始化。

  • 賦值和初始化:

 C++規定,對象的成員變量的初始化動作發生在進入構造函數本體之前。所以應將成員變量的初始化置於構造函數的初始化列表中。
     ABEntry::ABEntry(conststd::string& name, const std::string& address,conststd::list<PhoneNumber>& phones)
 { 
            theName = name;//這些都是賦值,而非初始化

            theAddress = address;//這些成員變量在進入函數體之前已調用默認構造函數,接着又調用賦值函數。
            thePhones = phones;
            numTimesConsulted = 0;
    } 

ABEntry::ABEntry(const std::string& name, conststd::string& address,conststd::list<PhoneNumber>& phones) 
        : theName(name),  //
這些纔是初始化 
        theAddress(address), //
這些成員變量只用相應的值進行拷貝構造函數,所以通常效率更高。
        thePhones(phones),
        numTimesConsulted(0){    } 

如果成員變量時const或reference,它們就一定需要初值,不能被賦值。 C++有着十分固定的“成員初始化次序”。次序總是相同:base classes更早於其derivedclasses被初始化,而class的成員變量總是以其聲明次序被初始化。當在成員初始化列表中列各成員時,最好總是以其聲明次序爲次序。

  • non-local static對象初始化次序

如果某編譯單元內的某個non-local static對象的初始化動作使用了另一編譯單元內的某個non-local static對象,它所用到的這個對象可能尚未初始化,因爲c++對“定義於不同編譯單元內的non-local static對象”的初始化次序並無明確定義。

將每個non-local static對象搬到自己的專屬函數內(該對象在次函數內被聲明爲static)。這些函數返回一個reference指向它所含的對象。所以non-local static對象被local static對象替換了。這是Singleton模式的一個常見實現手法。

任何一種non-const static對象,不論它是local或non-local,在多線程環境下“等待某事發生”都會有麻煩。處理的一種做法是:在程序的單線程啓動階段手工調用所有reference-returning函數,這可消除與初始化有關的“競速形勢”。

請記住:

  • 爲內置對象進行手工初始化,因爲C++不保證初始化它們;
  • 構造函數最好使用成員初始化列表,而不要在構造函數本體內使用賦值操作。初始化列表列出的成員變量,其排列次序應該和它們在類中的聲明次序相同;
  • 爲免除“跨編譯單元之初始化次序”問題,請以local static對象替換non-local static對象。   


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章