【學習筆記】Effective C++

【Effective C++】學習筆記

《Effective C++》,作者Scott Meyers

目錄:

  • 第一課:讓自己習慣C++ (簡單介紹)
  • 第二課:構造/析構/賦值運算
  • 第三課:資源管理
  • 第四課:設計與申明
  • 第五課:實現
  • 第六課:繼承與面向對象設計
  • 第七課:模板與泛型編程
  • 第八課:定製new和delete
  • 第九課:雜項討論

第一課:讓自己習慣C++
條款01、視C++爲一個語言聯邦
條款02、儘量以const、enum、inline替換#define
#define是使用預處理器進行處理,無法被編譯器知道,因此在發生錯誤時無法進行追溯。

條款03、儘可能使用const
const修飾指針,指針本身不能改變:char* const p
const修飾內容,指針所指物不能改變(明確來講應該是不能通過指針來改變所指物的值):const char* p=char const *p
簡單總結:const放在星號之前表示修飾內容,const放在星號之後表示修飾指針。
const的使用,
const修飾迭代器
1、因爲迭代器的功能和指針類似,所以const修飾迭代器就和修飾指針有同樣的功能,即該迭代器不能指向其他的內容:
const std::vector::iterator iter=vec.begin();
++iter 該指令是錯誤的,因爲該迭代器不能指向別的內容(失去了迭代器的原有功效,所以很少用)
2、若希望所指代的內容不能被改變,則需要使用const_iterator:
std::vector::const_iterator cIter=vec.begin();
*cIter=10 該指令是錯誤的,因爲不能改變所指向的內容
const修飾函數,函數返回值、各參數和函數自身(類的成員函數)
1、函數的返回值:
若函數返回的是一個具體的值,對其進行const修飾沒有任何意義,因爲返回的值都是進行復制進行賦值的;
或者可以配合值的引用一起使用&,代表返回的是值的引用。

const A &GetA(void)

如果返回的是一個指針,則有一定的意義,該返回值只能被賦給加const修飾的同類型指針。例如函數:

const char * GetString(void);

如下語句將出現編譯錯誤:

char *str = GetString();

正確的用法是

const char  *str =GetString();

2、函數的參數:
函數輸入參數的修飾,與值的修飾和指針的修飾是一樣的道理。常與函數的引用傳遞&一起使用,如:

void Func(const int &x)

3、類的成員函數:

int get() const{ ... }

代表該函數不能修改對象的數據成員而且不能調用非const函數。任何不會修改數據的成員函數都應該使用const進行修飾。
const修飾this是本質,至於說“表示該成員函數不會修改類的數據。否則會編譯報錯”之類的說法只是一個現象,根源就是因爲this是const類型的
但是有一種例外的情況就是const成員函數可以改變成員變量,即使用mutable進行修飾,例變量year:
mutable int year;
則year變量在const函數裏面可以被改變。

總結
1、const一般和值的引用&一起進行使用,目的是提高程序的效率的同時又不改變值;
2、const修飾成員函數的主要作用是避免成員函數改變成員變量的值,其根源是修飾*this爲const。再者,const對象只能調用const成員函數;
3、相對應的知識還有const_cast、static_cast等強制類型的轉換。

條款04、確定對象被使用前已經被初始化
初始化是一個比較複雜的問題,需要後面的知識作爲背書。
初始化的問題,一般是落在構造函數身上。
1、構造函數總是使用成員初值列對成員進行初始化;
2、如果成員變量是const或reference,他們就一定需要初值,不能被賦值;

條款05、瞭解C++默認編寫並調用了哪些函數
C++類中會默認生成:

  1. 默認構造函數;
  2. copy構造函數;
  3. copy assignment操作符;
  4. 析構函數。

條款06、若不想使用編譯器自動生成的函數,就該明確拒絕
對於默認生成的函數,如果不想使用該功能,就應該明確進行拒絕!!
如何拒絕呢?
比如說現在創建的類需要禁止copy操作,比如銀行賬戶,賬戶與賬戶之間是禁止進行copy的。如果不申明copy構造函數,會有默認的copy構造函數。一種做法就是**在private下申明copy構造函數,並且不去定義它。**例如:

class base 
{
public:
	base() {};
private:
	base(const base &);
};

int main() 
{
	base a;
	base b(a);
	return 0;
}

編譯會進行報錯:
在這裏插入圖片描述
更好的做法是私有繼承一個不能拷貝的類,這樣不僅外部不能訪問copy構造函數,內部成員函數也不能訪問基類私有傷員函數copy構造函數,這樣在編譯的過程中就會報錯。

條款07、爲多態基類申明virtual析構函數
現在有兩個類,一個是基類base class,另一個是派生類derived class。基類指針或引用既能夠指向base又可以指向derived,虛析構函數的作用是當基類指針指向派生類時調用的是派生類的析構函數,這就達到了內存釋放的多態性。
當base class裏有一個函數是虛函數是,析構函數就應該設置爲虛析構函數;如果沒有多態性的考量,就不應該設置爲虛析構函數。

條款08、別讓異常逃離析構函數
儘量不要讓析構函數法發生異常(或者吐出異常)。
爲什麼?析構函數發生異常會導致程序過早接續或者出現不明確行爲。
怎麼解決?
1、當發生異常時使用abort()函數結束程序或者吞下異常;
2、不要讓析構函數有機會拋出異常,可以另外設計成員函數解決此功能。

條款09、絕不在構造和析構過程中調用cirtual函數
以構造函數爲例。
對於基類和派生類,當派生類調用構造函數時,首先會調用基類的構造函數,然後再調用派生類的構造函數。如果構造函數中有虛函數,就相當於派生類使用的是基類定義的虛函數。會引發不知名的錯誤。
可以理解爲base class在構造期間,virtual函數不是virtual函數。
怎麼解決?
不使用虛函數而要實現虛函數的功能,可以令派生類將必要的信息傳遞到base class構造函數,加以彌補。

*條款10、令operator=返回一個reference to this
主要是爲了解決連鎖賦值的問題。

條款11、再operator=中處理“自我賦值”
自我賦值就是自己給自己賦值。
在賦值的過程中,可能會有delete的操作。如果賦值的左右兩邊是同一個對象,那麼delete操作就會引發錯誤。
怎麼做?
1、一種做法就是在賦值之前進行證同檢測(證明是否一樣,這是傳統的做法);
2、先生成一個副本,然後再賦值。

條款12、複製對象時切勿忘其每一個成分
對於拷貝構造函數和拷貝賦值操作符,都是需要對對象進行復制。
1、在派生類中的構造函數,不僅僅要複製本地的變量,還要複製基類的數據;
2、拷貝構造函數和拷貝賦值操作符不要進行相互調用的情況。

條款13、
條款14、
條款15、
條款16、
條款17、
條款18、
條款19、
條款20、

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