天天看點

Effective c++ 第一章總結

1.視C++為一個語言聯邦。

由:C,Object-Oriented C++,Template C++,STL組成

每個部分的函數傳值都不一樣:

C(内置資料類型):pass-by-value

Object-Oriented C++(對象類型):pass-by-reference-to-const

Template C++(泛型):pass-by-reference-to-const

STL(指針類型):pass-by-value

2.盡量以const,enum,inline替換#define

原因:

a.#define 不友善調試,當發現#define的代碼有問題,不能定位到具體是哪裡的問題。

const變量的定義:

const double aspectRatio=1.653;

const char* const authorName="Scott Meyers";

const std::string authorName("Scott Meyers");一般用這個替換上面的常量字元串定義

b.#define 不能建立class的專屬常量,因為#define并不重視作用域,不能提供任何封裝性。

用const定義class的專屬常量:

class GamePlayer

{

private:

static const int NumTurns=5;//常量聲明式

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

...

};

注意:通常C++要求你對你所使用的任何東西提供一個定義式

但是隻要滿足3個條件(1.屬于class的專屬常量 2.是static類型 3.是整數類(integral type:ints,chars,bools))就無需定義式。

但如果你要取class專屬常量的位址,或縱使你不取其位址而你的編譯器堅持要定義式,那就必須提供定義式。

上面類的NumTurns參數的定義式如下:

const int GamePlayer::NumTurns;//定義式,先前已經指派,這裡不能再指派了。

不過編譯器不支援在聲明式賦初值,則可以将初值移動到定義式裡面。這是上面的類中就不能定義數組了,可以定義如下:

class GamePlayer

{

private:

enum{ NumTurns = 5 };//常量聲明式

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

...

};

如果你不想讓别人獲得一個pointer或者reference指向你的某個整數常量,enum可以幫助你實作這個限制。

c.有些宏定義的像函數,這樣可能會導緻不可思議的結果。

例:

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

int a=5,b=0;

CALL_WITH_MAX(++a,b);//a 被累加2次

改為:

template<typename T> //由于我們不知道

inline void callWithMax(const T& a,const T& b) //T是什麼,是以采用

{ //pass by reference to const

f(a > b ? a : b);

}

總結:

a.對于單純的常量,最好以const對象或者enum 替換 #define

b.對于形似函數的宏,最好改用inline函數替換 #define;

3.盡可能使用const.

std::vector<int> vec;

...

const std::vector<int>::iterator iter=vec.begin();//iter的作用像個T* const

*iter=10;//沒問題

++iter;//錯誤

std::vector<int>::const_iterator iter=vec.begin();//iter的作用像個const T*;

*iter=10;//錯誤

++iter;//沒問題

const 最具威力的用法是面對函數聲明時的應用。在一個函數聲明式内,const可以和函數傳回值,各參數,函數自身(如果是成員函數)參數關聯。

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

例:

class Rational{..};

const Rational operator* (const Rational& lhs,const Rational& rhs);

如果不這樣定義,下面的表達式是成立的:

Rational a,b,c;

(a*b)=c;//因為a*b傳回的是Rational類型,是以可以被指派,如果加了const就變成常量了,是不能被指派的。

b.将const實施于成員函數的目的,是為了确認該成員函數可作用于const對象身上。

第一,知道哪個函數可以改動對象,哪個函數不行,很是重要。

第二,它們使“操作const對象”成為可能。

許多人漠視一件事實,兩個成員函數如果隻是常量性不同,可以被重載。

class TextBlock

{

public:

...

const char& operator[](std::size_t position) const //operator[] for

{ return text[position]; }   //const對象

char& operator[](std::size_t position)   //operator[] for

{ return text[position]; }   //non-const對象

//以上函數還有一個實作方式

char& operator[](std::size_t position)   //operator[] for

{ return   //non-const對象

const_cast<char&>(static_cast<const TextBlock&>(*this)[position]); }  

private:

std::string text;

};

TextBlock tb("Hello");

std::cout<<tb[0];//調用non-const函數

const TextBlock tb("Hello");

std::cout<<tb[0];//調用const函數

如果函數的傳回類型是個内置類型,那麼改動函數的傳回值從來就不合法。

如果想在一個const成員函數中修改成員變量,則可将變量定義為 mutable 類型。

const成員函數承諾絕對不改變其對象的邏輯狀态。

總結:

a.将某些東西聲明為 const 可幫助編譯器偵測出錯誤用法。const可被施加于任何作用域内的對象,函數參數,函數傳回類型,成員函數本體。

b.編譯器強制實施 bitwise constness,但你編寫程式時應該使用“概念上的常量性”。

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

4.确定對象被使用前已先被初始化

請立下一個規則,規定總是在初值列中列出所有的成員變量,并且賦初值。

如果成員變量是const或references,它們就一定需要在初值列中初始化,不能被指派。

許多class擁有多個構造函數,每個構造函數有自己的成員初值列。如果這鐘class存在許多成員變量或base class,多份成員初值列的存在就會導緻

不受歡迎的重複。這種情況下可以合理地在初值列中遺漏哪些“指派表現像初始化一樣好”的成員變量。改用他們的指派操作,并将哪些指派操作

移往某個函數(通常是private),供所有構造函數調用。這種做法在“成員變量的初值系由檔案或資料庫讀入”時特别有用。

C++ 有着十分固定的“成員初始化次序”。是的,次序總是相同:base classes更早于其derived classes被初始化,class的成員變量總是以其聲明

次序被初始化。

“不同編譯單元内定義之non-local static對象 ”的初始化次序:

static對象:其壽命從被構造出來直到程式結束為止,析構函數會在main()函數結束時被自動調用。stack和heap-based對象都被排除。這種對象包括global對象,namespace作用域對象,class内對象,函數内對象,file作用域内對象。

local static對象:函數内的static對象,因為他們對函數而言是local。

non-local static對象:local static對象外的其他對象。

編譯單元:指産出單一目标檔案的那些源碼。基本上它是單一源碼檔案加上其所含入的頭檔案。

C++ 對“定義于不同的編譯編譯單元内的non-local static對象”的初始化相對次序并無明确定義。針對這個問題可以用單例模式來解決。

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

class CFileSystem{};

CFileSystem& tfs()

{

static CFileSystem fs;

return fs;

}

不同編譯單元内定義的non-local static 對象的初始化順序

總結:為避免在對象初始化之前過早地使用它們,你需要做3件事。

第一,手工初始化内置型(non-member)對象。

第二,使用成員初值列對付對象的所有成分。

第三,在初始化次序不确定性時加強你的設計。(不同編譯單元所定義的non-local static對象的初始化順序不确定,要用單例模式解決)

繼續閱讀