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頭文件,哎呀我去!!!!
*/