Essential C++濃縮筆記(五)——面向對象的編程風格

 

1、指向class member function的指針

pointer to member function的聲明,和pointer to non-member function不同的是需要指定它指向的class對象。

 //pm聲明爲一個指針,指向num_sequence的成員函數,後者的返回了類型必須是void,函數參數爲int類型
void (num_sequence::*pm)(int) = 0;   

//另一種定義方式
typedef void (num_sequence::*PtrType)(int) = 0;   
PtrType pm =0;

 取member function地址需要用class scope

pm = &num_sequence::triangular;    //pm指向triangular函數

 

2、面向對象的編程思維

默認情況下,member function的解析都在編譯時靜態的完成,如果要令在運行時動態進行,在聲明前加上關鍵字virtual;

程序定義的派生對象,基類和派生類的constructor和destructor函數都會被執行。

protected關鍵字:被聲明爲protected的成員僅可以被派生類直接訪問

 

特殊轉換記號: static_cast<new_type> (expression)

 

3、定義抽象基類

定義抽象基類的步驟

1)找出所有子類共通的操作行爲

2)找出操作行爲與哪些類型有關,哪些操作行爲必須根據不同的派生類而有不同的實現方式,設置成爲虛函數

3)確定訪問層級,public、private、protected;

只能在派生類中才能通過派生類對象訪問基類的protected成員!!!

static member function不能被聲明爲虛擬函數!!!

凡基類定義有一個虛函數,應該要將destructor聲明爲虛函數,但是不要是純虛函數。

 

純虛函數:任何類如果聲明有純虛函數,程序無法爲它產生任何對象,這種類只能作爲派生類的子對象使用,而且前提是這些派生類必須爲所有虛函數提供確切的定義。

這篇文章有更深的討論;https://blog.csdn.net/hackbuteer1/article/details/7558868

 

4、定義派生類

類進行繼承聲明之前,基類的定義必須已經存在。

在類之外對虛函數進行定義時,不必指明關鍵詞virtual。

每當派生類中有某個member與基類的member同名,那麼會遮掩基類的那份member,如果要在派生類內使用繼承來的那份member,必須使用class scope加以限定。

Data member 如果是個reference,必須在構造函數的memberintialization list中加以初始化。一旦初始化,就再也無法指向另一個對象。如果data member是個pointer,就無此限制:可以在構造哈桑內加以初始化, 也可以先將它初始化爲nulll。

 

5、初始化

如果抽象基類中有實際的data member,那麼必須要提供初始化,若將初始化留給派生類,可能有隱患。恰當的設計方法是爲基類提供構造函數,利用這個構造函數處理基類所聲明的所有data member的初始化操作。

抽象基類無法爲它定義任何對象,它扮演的角色是每個派生類對象的子對象,基於此,將抽象基類的構造函數聲明爲protected而不是public。

派生類的構造函數,不僅要爲派生類的data member進行初始化操作,還需要爲基類的data member提供適當的值。

//基類有三個data member,派生類有兩個data member
inline Fibonacci::Fibonacci(int len,int beg_pos):num_sequence(len,beg_pos,_elems){}

 另一種初始化的方法是提供默認構造函數。

 

6、在派生類中定義一個虛函數

如果要覆蓋基類提供的虛函數,那麼派生類提供的新定義,函數原型必須完全符合基類所聲明的函數原型,包括參數列表、返回類型、常量性。 要麼無法發揮虛函數機制,要麼無法編譯成功。

在派生類中,爲了覆蓋基類的某個虛函數,而進行聲明操作,不一定得加上關鍵字Virtual。編譯器會根據兩個函數的原型聲明,決定某個函數是否會覆蓋基類中的同名函數。

 

有兩種情況,虛函數不會出現預期行爲,1)基類的構造和折構函數 2)使用的是基類的對象,而不是針對對象的pointer或reference時。【如果傳入的是派生對象,可能沒有足夠的內存放置派生類的data member】

 

 

7、 編程時遇到的問題及總結

1、switch中default的位置:https://www.cnblogs.com/LubinLew/p/default_in_switch.html

結論是:default在case全不匹配的情況下進入;可以放在任意位置;進入後和普通進入點一樣,如果沒有break則繼續執行

2、protected的訪問權限 https://blog.csdn.net/luoruiyi2008/article/details/7179788

3、無法實例化抽象類 

https://blog.csdn.net/m0_38129013/article/details/78490931

如果抽象類的派生類有任何一個純虛函數沒有實現,那麼這個類仍然是一個抽象類

https://blog.csdn.net/wangshubo1989/article/details/49953095

4、有關char*

const char* num_sequence::what_am_i() const {
	const char *names[num_seq] = { 
		"noset","fibonacci","pell","lucas","triangular","square","pentagonal" 
	};
	return names[_isa];		
}

之前總覺得 上面的代碼有點奇怪,字符串常量的本質表示其實是一個地址。

所以有:

char  *s ;
s = "China";        //真正的意義是 s ="China" = 0x3000(地址);

5、一個很低級的錯誤,找不到“valType”類型的右操作數的運算符,是因爲沒有包含string頭文件

/*ERROR錯誤說明
調用:
	BinaryTree< string > bt;
	...
	bt.insert("Piglet");	
	cout << "preorder traversal: \n";
	bt.preorder();

//	preorder函數體內有display_val函數,錯誤指示到display_val函數內:

	display_val(BTnode *pt, ostream &os) const
	{
		os << pt->_val;
		if (pt->_cnt > 1)
			os << "( " << pt->_cnt << " ) ";
		else os << ' ';
	}

	錯誤:C2679 二進制“ << ”: 沒有找到接受“valType”類型的右操作數的運算符(或沒有可接受的轉換)
	原因:沒有包含string頭文件,哎呀我去!!!!

*/

 

發佈了31 篇原創文章 · 獲贊 143 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章