1.條款03:儘可能使用const
1.1 const能夠修飾指針,指針所指物,二者或者都不是const對象。注意const的位置,位於*號左邊的是修飾所指物爲常量, *右邊是修飾指針
const char* p = greeting //const data, non-const pointer
char* const p = greeting //const pointer, non-const data
const char* const p = greeting // all the const
例子,以迭代器中的T*指針來講明具體的用法:
std::vector<int> vec;
...
const std::vector<int>::iterator iter = vec.begin(); // iter做用是T* const
*iter = 10; //能夠改變指針所指
iter++; //報錯,不能修改iter常量
std::vector<int>::const_iterator cIter = vec.begin(); //cIter做用是const T*
*cIter = 10; //不能夠修改指針指向
cIter++; //沒問題,能夠改變所指物
下列兩個函數的參數所返回的參數類型是一致的
void f1(const Widget* pw) // f1和f2都返回了一個指針指向常量Widget
void f2(Widget const* pw)
1.2 const成員函數: 經過聲明const成員函數有兩個好處,第一:使得class接口更容易被理 解,什麼內容是不能被修改的 第二:提高C++效率,pass-by-reference-to-const. PS:兩個成員函數若是是常量,同樣能夠被重載。
1.2.1 bitwise constness and logical constness
2.條款04:肯定對象被使用前已經被初始化Make sure that objects are initialized before they're used.
2.1對於內置類型以外的對象的初始化,主要由構造函數來實現,因此確保每一個構造函數把全部的成員變量初始化。
2.2【理解】注意不要混淆了初始化(initialization)和賦值(assignment),參考下面的例子:
賦值操做:
class PhoneNumber{...};
class ABEntry{
private:
std::string theName;
std::string theAddress;
std::List<PhoneNumber> thePhones;
int numTimesConsulted;
public:
ABEntry(const std::string& name, const std::string& address, const std::List<PhoneNumber>& phones);
};
//構造函數進行賦值(assignments)而並不是是初始化(initializations)
ABEntry::ABEntry(const std::string& name, const std::string& address, const std::List<PhoneNumber>& phones){
theName = name;
theAddress = address
thePhones = phones;
numTimesConsulted = 0;
}
//構造函數初始化的最佳寫法是使用MemberInitialization List(成員初始化列表)的方式實現
ABEntry::ABEntry(const std::string& name, const std::string& address, const std::List<PhoneNumber>& phones)
: theName(name), theAddress(address), thePhones(phones), numTimesConsulted(0)
{}
這個構造函數比上面的效率更高!上面那個基於賦值的那個構造函數要首先調用一個default默認構造函數設置成員變量初始值。
2.3 爲免除「跨編譯單元之初始化次序」問題,以local static對象替換non-local static對象
第二:構造函數,析構函數,賦值
1.條款05 瞭解C++默默調用了哪些函數 Know what functions C++ silently writes and calls
1.1 空類Empty class,C++處理以後會爲它添加默認的構造函數,一個賦值操做符和一個析構函數。
若是你寫了 class Empty{}; 通過C++處理後至關於:
class Empty{
public:
Empty(){...}; //default構造函數
Empty(const Empty& rhs){..}; //copy構造函數
~Empty(){...}; //析構函數,是否爲Virtual?
Empty& operator=(const Empty& rhs){} //copy assignment操做符
}
【請記住】:編譯器暗自爲class建立default構造函數,copy構造函數,析構函數和copy assignments操做符。
2.條款06 若不想使用編譯器自動生成的函數,就該明確拒絕。
爲了避免使用編譯器自動提供建立函數的功能,可將相應的成員函數聲明爲private而且不予實現。使用像Uncopyable這樣的的base class也是一個方法。
//Uncopyable實現阻止編譯器自動建立函數
class Uncopyable{
protected:
Uncopyable(){};
~Uncopyable(){};
private:
Uncopyable(const Uncopyable&); //阻止copy構造函數
Uncopyable& operator=(const Unconpyable&);
}
爲了防止HomeSale對象被拷貝,經過繼承Uncopyable類即可以免
class Homesale: private Uncopyable{
... //class將再也不自動聲明 copy構造函數以及copy assignment操做符。
...
}
3. 條款07 爲多態基類聲明virtual析構函數(Declare destructors virtual in polymorphic base classes)
【請記住】:
3.1 Polymorphic(帶多態性質的)的基類base class,應該聲明一個virtual析構函數。若是一個class存在任何一個virtual函數,避免由於子類繼承過程當中帶來的問題。應該擁有聲明一個virtual析構函數。
3.2 Classes設計的目的若是不是爲了實現多態做爲基類來使用,就不應聲明virtual析構函數。
錯誤使用方法
class SpecialString: public std::string{ //餿主意!std::string中有個non-virtual函數
....
};
SpecialString* pw = new SpecialString("Impending Doom");
std::string* ps;
...
ps = pw; //SpecialString* = string*
delete ps; //未定義!*ps的SpecialString資源會泄露
//由於SpeicalString的析構函數沒被調用
4. 條款08 別讓異常逃離析構函數(Prevents exceptions from leaving deconstructors)
【請記住】
4.1 析構函數絕對不要吐出異常,若是一個被析構函數調用的函數可能拋出異常,那麼析構函數應當捕捉任何異常,而後吞下他們或者結束程序。
4.2 若是客戶須要對某個操做函數運行期間拋出的異常作出反應,那麼class應當提供一個普通函數(而非在析構函數中)處理。