(Effective C++)第一章 讓自己習慣C++(View Yourself to C++)

1.1 條款1:視C++爲一個語言聯邦(View C++ as a federation of language)

C++主要的次語言(sublanguage):
l  【3-1-1】 說到底C++仍是以C爲基礎。區塊(blocks),語句 (statements),預處理器(preproccessor),內置數據類型(built-in data types),數組(arrays),指針(pointers)等都來自於C。
l  【3-1-2】 Object-Oriented C++.類classes,封裝(encapsulation),繼承(inheritance),多態(polymorphism),virtual函數(動態綁定)等等
l  【3-1-3】 Template C++這是泛型編程(genericprogramming)部分,它帶來了嶄新的編程範型(programming paradigm),也就是所謂的template metaprogramming(TMP,模板元編程)。
l  【3-1-4】 STL是個template程序庫。它對容器(containers),迭代器(iterators),算法(algorithms)以及函數對象(function objects)。

1.2 條款2:儘量使用consts,enums和inlines,少用#define (Prefer consts,enums and inlines to #define)

在C++程序中,儘量使用consts,enums和inlines,少用#define。
l  【3-2-1】對於單純常量,最好以const對象或enum替換#defines。
l  【3-2-1】對於形似函數的宏(macros),最好改用inline函數。

例如:
const double PAI = 3.1415;
const char* const HELLO = "Hello";
const std::string AUTHOR_NAME = "wuzhenghua";
class CAnimal
{
     Private:
         const int COLOR = 1;
};
示例3-2-1 常量

我們無法利用宏#define創建一個class專屬常量,因爲#define並不重視作用域(scope)。
一個屬於枚舉類型的數值可以冒充ints使用。枚舉的行爲比較像#define而不像const。例如,取一個const的地址是合法的,但是取一個enum的地址就不合法,而取#define的地址也是不合法的。
#define函數一是語句引起的歧義,二是沒有安全類型檢查。所以最好使用inline函數,如下:  

template<typename T>
Inline  void MAX(const T& a, const T &b)
{
    f(a > b ? a:b);
}
示例3-2-2 inline函數

1.3 條款3:儘可能使用const(Use const wheneverpossible)

Const的一件奇妙事情是,它允許你指定一個語義約束(也就是指定一個“不被改動”的對象),而編譯器會強制實施這項約束。

1.3.1 const與指針

面對指針,修飾指針,或修飾指針所指物。如果關鍵值const出現在星號左邊,表示被指物是常量;如果出現在星號右邊,表示指針自身是常量;如果出現在星號兩邊,表示兩者都是常量。

char greeting[]="Hello";
char *p = greeting;                 //non-const pointer, non-const data
const char *p = greeting;           //non-const pointer, nconst data
char * const p = greeting;          //const pointer, non-const data
const char * const p = greeting;    //const pointer, const data
示例3-3-1 const與指針變量

1.3.2 const與迭代器

如果你希望迭代器所指的東西不可被改動(即是希望STL模擬一個const T* 指針),你得使用const_iterator:   
std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin(); // iter像T*const
*iter = 10;       //正確
iter++;           //錯誤
std::vector<int>::const_iterator cIter = vec.begin();// iter像const T*
*cIter = 10;       //錯誤
cIter++;           //正確
示例3-3-2 const與迭代器

令函數返回一個常量值,可以降低因客戶錯誤而造成的意外,而不置於放棄安全性和高效性。
class Rational {…};
const Rational operator*(const Rational& lhs, const Rational & rhs);
Rational a,b,c;       //正確
if(a*b=c)  //其實想做一個比較而掉了一個=,編譯器報錯
示例3-3-3 const與operator*

1.3.3 const與成員函數

理由:第一,const是class接口比較容易被理解;第二,const使“操作const對象”成爲可能。兩個成員函數如果只是常量性(constness)不同,可以被重載。這是C++的一個重要特性。const成員函數不可以更改對象內任何non-static成員變量。

const與non-const成員函數應該避免代碼重複。
Class TextBlock {
public:

const char & operator[](std::size_t pos) const
{
   …
}
char & operator[](std::size_t pos)
{
   return const_cast<char &>  //將op[]返回值的const去除
          (static_cast<const TextBlock &>(*this) //爲*this加上const
          [pos];
}

}
示例3-3-4 const與成員函數

【注意】 non-const成員函數本來就可以對其對象做任何動作,所以在其中調用一個const成員函數並不會帶來風險。

1.4 條款4:關於“將對象初始化”這事,C++似乎反覆無常(Make sure that object areinitialized before they’re used)

關於“將對象初始化”這事,C++似乎反覆無常。
確保每個構造函數都將對象的每一個成員初始化。但是,別混淆賦值(assignment)和初始化(initiation)。
class ABEntry{
public:
ABEntry(const std::string &name, const std::list<PhoneNuber> &phones );
private:
    std::string theName;
    std::list<PhoneNuber> thePhones;
    int numTimesConsulted;
}
ABEntry(const std::string &name, const std::list<PhoneNuber> &phones ){              //這些都是賦值
theName = name;
thePhones = phones;
numTimesConsulted = 0;
}
ABEntry(const std::string &name, const std::list<PhoneNuber> &phones ): theName(name), thePhones(phones), numTimesConsulted(0)
{               //這些都是初始化

}
示例3-4-1 賦值與初始化

C++規定,對象的成員變量的初始化動作發生在進入構造函數本體之前。成員初始化列(memberinitialization list)比賦值效率高,但是內置類型的效率是一樣的。

一旦你已經很小心將“內置型成員變量”明確地加以初始化,而且也確保你的構造函數運用“成員初始列”初始化base class和成員 變量,那就只剩下“不同編譯單元內定義之non-conststatic對象”的初始化次序。

所謂static對象,其壽命從被構造出來直到程序結束爲止。這種對象包括global對象,定義於namespace作用域內的對象,在class內,在函數內,以及在文件作用域內被聲明static的對象。

函數內的static對象稱爲local static對象,其他稱爲non-local static對象。

所謂編譯單元(translationunit)是指產出單一目標文件(single object file)的那些源碼。

C++對定義於不同編譯單元內的non-local static對象的初始化次序沒有明確定義。

可以解決該問題的方法:將每個non-localstatic對象搬到自己的專屬函數內(該對象在此函數聲明爲static)。這些函數返回一個reference指向它所包含的對象。然後,用戶調用這些函數,而不直接涉及這些對象。

C++保證:函數內的local static對象會在“該函數被調用期間”“首次遇上該對象之定義式”時被初始化。

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