界面類 和 數據類 分開
******************************************************************************************************************
******************************************************************************************************************
More Effective C++ 筆記
******************************************************************************************************************
******************************************************************************************************************
條款1.限制某個 class 所能產生的對象數量
1)類中的靜態成員總是被構造,即使不使用,而且你無法確定它什麼時候初始化;
2)非成員內聯函數在鏈接的時候在目標文件中會產生多個副本,可能造成程序的靜態對象拷貝超過一個。
3)限制對象個數:建立一個基類,構造函數和復制構造函數中計數加一,若超過最大值則拋出異常;析構函數中計數減一。
4)允許一個或零個對象,阻止某個類的對象被創建的最簡單方法就是把這個類的構造函數聲明為私有(private)。
************************************************************************
條款2.限制某個 class 所能產生的對象數量了解 virtual functions、multiple inheritance、virtual base classes、runtime type identification 的成本
1)不要將虛函數聲明為 inline ,因為虛函數是運行時綁定的,而 inline 是編譯時展開的,即使你對虛函數使用 inline ,編譯器也通常會忽略。
條款3.考慮使用其它程序庫
選用不同的庫可能會大幅改善程序性能,比如說 iostream 和 stdio 庫.
******************************************************************************************************************
------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------
Effective C++ 筆記
------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------
******************************************************************************************************************
條款1.盡量用:const和inline 不用:#define
const char * const PRESCRIPTION_STATE_ = "正在配置";
static const int LIST_NUM = 5;//類中聲明
char *szPatientArray[LIST_NUM];//使用
const int 類名::LIST_NUM;//定義
enum{LIST_NUM = 5}
char *szPatientArray[LIST_NUM];
template
inline const T& Max(const T&a, const T&b)
{
return a>b ? a:b;
}
******************************************************************************************************************
條款2.盡量用#include
論 cin/cout 和scanf/print的優缺點
#include
#include
******************************************************************************************************************
條款3.盡量用new和delete 不用 malloc和free
******************************************************************************************************************
條款5.對應的new和delete要采用相同的形式
delete stringptr1;// 刪除一個對象
delete [] stringptr2;// 刪除對象數組
******************************************************************************************************************
條款6:析構函數裡對指針成員調用delete
除非對指針變量用了new, 否則是不需要用delete的
******************************************************************************************************************
條款11.為需要動態分配內存的類聲明一個拷貝構造函數和一個賦值操作符
如果沒重載操作符
string a("hello");
string b("world");
b=a;//b內存沒釋放,就指向了a
string a("hello"); // 定義並構造 a
{ // 開一個新的生存空間
string b("world"); // 定義並構造 b
b = a; // 執行 operator=
} // 丟失b的內存 // 離開生存空間, 調用 b的析構函數
string c = a; // c.data 的值不能確定! // a.data 已被刪除
void donothing(string localstring) {}
string s = "the truth is out there";
donothing(s); //donothing()執行結束,localstring被刪除,s包含一個指向 localstring早已刪除的內存的指針。
*
只要類裡有指針時,就要寫自己版本的拷貝構造函數和賦值操作符函數
當實現拷貝構造函數和賦值操作符非常麻煩的時候,特別是可以確信程序中不會做拷貝和賦值操作的時候,只聲明這些函數(聲明為private成員)而不去定義(實現)它們
私有的拷貝構造函數,不支持對象拷貝
private:
CIniFile(const CIniFile&){}
******************************************************************************************************************
條款12: 盡量使用初始化而不要在構造函數裡賦值
1)const成員只能被初始化,不能被賦值。
2)沒有為CString 對象指定初始化參數,CString的缺省構造函數會被調用。然後再調用類構造函數賦值,導致兩次調用CString函數
3)static類成員永遠也不會在類的構造函數初始化
******************************************************************************************************************
條款13: 初始化列表中成員列出的順序和它們在類中聲明的順序相同
規則:類成員是按照它們在類裡被聲明的順序進行初始化的,和它們在成員初始化列表中列出的順序沒一點關系。
規則:對一個對象的所有成員來說,它們的析構函數被調用的順序總是和它們在構造函數裡被創建的順序相反。
******************************************************************************************************************
條款14: 確定基類有虛析構函數
當通過基類對象去刪除派生類對象時,如果基類對象沒有虛析構函數,那麼結果是不可預測的。此時派生類的析構函數不被調用。
當一個類作為基類時,需要虛析構函數,目的是讓派生定義自己的析構函數
當一個類不作為基類時,不需要析構函數
****************************************************************************************************************--2016.3.16--
條款16: 在operator=中對所有數據成員賦值
1)*ptr = *rhs.ptr; // 對於ptr,賦的值是指針所指的值,不是指針本身
2)派生類調用基類的operator=
derived& derived::operator=(const derived& rhs)
{
if (this == &rhs) return *this;
static_cast
y = rhs.y;
return *this;
}
3)派生類拷貝構造函數需要對基類也做拷貝,否則調用基類的缺省構造函數
derived(const derived& rhs): base(rhs), y(rhs.y) {}
******************************************************************************************************************--2016.3.22--
條款18: 類的接口完整 並且最小(少) (類要易於理解,易於使用,易於實現, 功能要強大,要方便使用)
函數的數量要盡可能地少,每一個函數都完成各自不同的任務.
不時增加函數以提供對各種通用功能的支持.
1)典型的接口裡只有函數存在,因為在用戶接口裡放上數據成員會有很多缺點
2)一個完整的接口是指那種允許用戶做他們想做的任何合理的事情的接口.
一個最小的接口,是指那種函數盡可能少、每兩個函數都沒有重疊功能的接口。
3)無端地在接口裡增加函數是有代價的
4)友元函數在所有實際應用中都是類的接口的一部分。這意味著友元函數影響著類的接口的完整性和最小性。
****************************************************************************************************************** --2016.3.22--
條款19: 分清成員函數,非成員函數和友元函數
1)成員函數可以是虛擬的,而非成員函數不行(虛函數必須是成員函數)。
2)explicit的構造函數不能用於隱式轉換。
3)如果需要的話,編譯器會對每個函數的每個參數執行一種隱式類型轉換。
4)能避免使用友元函數就要避免
const rational operator*(const rational& lhs,const rational& rhs)
{
return rational(lhs.numerator() * rhs.numerator(),lhs.denominator() * rhs.denominator());
}
5) 需要訪問非公有成員的非成員函數只能是類的友元函數。
6) 只有非成員函數對最左邊的參數進行類型轉換。
******************************************************************************************************************--2016.3.22--
條款20: 避免public接口出現數據成員
1) 用函數來獲取或設定數據成員的值,可以實現禁止訪問、只讀訪問和讀寫訪問等多種控制。甚至,如果你願意,還可以實現只寫訪問。
2)public接口裡放上數據成員無異於自找麻煩,所以要把數據成員安全地隱藏在與功能分離的高牆後
******************************************************************************************************************--2016.3.14--
條款21: 盡可能使用const
1) const出現在線的左邊,指針指向的數據為常量;
2) const出現在線的右邊,指針本身為常量;
3) const在現的兩邊都出現,二者都是常量。
4) 返回值是const: const rational operator*(const rational& lhs, const rational& rhs);
5) 參數值是const: const參數會導致一個臨時對象的產生;
6) 函數本身是const:const成員函數的目的當然是為了指明哪個成員函數可以在const對象上被調用
7)僅在const方面有不同的成員函數可以重載。
char& operator[](int position)
{
return data[position];
}
const char& operator[](int position) const
{
return data[position];
}
8)修改一個“返回值為固定類型”的函數的返回值絕對是不合法的。返回值必須為對象的引用
9)const成員函數禁止修改它所在對象的任何一個數據成員。
數據意義上的const(bitwise constness)
概念意義上的const(conceptual constness)
******************************************************************************************************************--2016.3.14--
條款22: 盡量用“傳引用”而不用“傳值”
非常高效:沒有構造函數或析構函數被調用,因為沒有新的對象被創建。
const student& ReturnStudent(const student& s)
{ return s; }
避免了所謂的“切割問題(slicing problem)”
當一個派生類的對象作為基類對象被傳遞時,它(派生類對象)的作為派生類所具有的行為特性會被“切割”掉,從而變成了一個簡單的基類對象。
引用幾乎都是通過指針來實現的,所以通過引用傳遞對象實際上是傳遞指針。因此,如果是一個很小的對象——例如int——傳值實際上會比傳引用更高效。
******************************************************************************************************************--2016.3.23--
條款24: 在函數重載和設定參數缺省值間慎重選擇
第一,確實有那麼一個值可以作為缺省嗎?
第二,要用到多少種算法?一般來說,如果可以選擇一個合適的缺省值並且只是用到一種算法,就使用缺省參數(參見條款38)。
否則,就使用函數重載。
“缺省”構造函數是憑空(沒有輸入)構造一個對象,而拷貝構造函數是根據一個已存在的對象構造一個對象
在重載函數中調用一個“為重載函數完成某些功能”的公共的底層函數
******************************************************************************************************************--2016.3.23--
條款26: 當心潛在的二義性
C++有一種思想:它認為潛在的二義性不是一種錯誤。
******************************************************************************************************************--2016.3.18--
條款31: 千萬不要返回局部對象的引用,也不要返回函數內部用new初始化的指針的引用
CPoint1 & operator=(const CPoint1& point)
{
CPoint1 cp1;
cp1.m_nX = point.m_nX*100;
cp1.m_nY = point.m_nY*100;
return cp1;
}
如果返回局部對象的引用,那個局部對象其實已經在函數調用者使用它之前被銷毀了。
寫一個返回廢棄指針的函數無異於坐等內存洩漏的來臨。
**************************************************************************************************************2016.3.24
條款30: 當數據成員是private或protect時,避免函數返回此數據成員的非const的 指針或引用
1)此時容易改變數據成員,破壞訪問方式。
2)可以通過返回指向const對象的指針或引用來達到完美效果
3)當要返回private或protect數據成員時,需在函數名稱前加上const。
******************************************************************************************************************
條款32: 盡可能地推遲變量的定義
將變量的定義推遲到必須使用它的時候,還要盡量推遲到可以為它提供一個初始化參數為止。
不僅可以避免對不必要的對象進行構造和析構,還可以避免無意義的對缺省構造函數的調用。而且,在對變量進行初始化的場合下,變量本身的用途不言自明,所以在這裡定義變量有益於表明變量的含義。
******************************************************************************************************************
條款37: 決不要重新定義繼承而來的非虛函數
任何條件下都要禁止重新定義繼承而來的非虛函數。
******************************************************************************************************************
其他:
#define “***” ***的第一個字符不能為數字
#pragma warning(push,4)