本文雖是起了個想要把C++中虛函數徹底透徹介紹的標題,但實際上只是本人記錄虛函數一些要點的文章。
最近讀了《Effective C++》,裏面有幾個關於虛函數的條款,以前對於虛函數不太瞭解,看了條款之後發現需要記錄下來加深自己的印象。
多態基類的析構函數要爲虛函數
這一點也正是多態的體現,假設有兩個類Father,Son(舉得例子不夠經典….):
class Father
{
public:
Father (){};
virtual ~Father(){};
}
class Son: public Father
{
string sonName;
int *a;
public:
Son(string name):sonName(name)
{
a =new int[10];
};
~Son()
{
delete a;
}
}
假設有以下代碼:
Son son;
Father *fa = &son;
delete fa;
若Father 類的析構函數不是虛函數,怎麼不能實現動態綁定,從而只會調用父類Father的析構函數,而不會調用子類的析構函數,從而造成內存泄露….
構造函數不能爲虛函數
同樣還是上面的例子,假設我們構造一個son對象,構造函數執行的過程是(省略參數…) Father() Son() 但是根據虛函數的情況,若構造函數爲虛函數,豈不是隻執行了Son的構造函數,不執Father()的構造函數…….這跟構造原理也出現了矛盾,因此構造函數是不能成爲虛函數的。
構造函數和析構函數中不要調用虛函數
上面的例子繼續擴展:
class Father
{
public:
Father (){makeMoney();};
virtual ~Father(){};
virtual void makeMoney(){ cout<<"father make money"};
}
class Son: public Father
{
string sonName;
int *a;
public:
Son(string name):sonName(name)
{
a =new int[10];
makeMoney();
};
~Son()
{
delete a;
}
virtual void makeMoney(){ cout<<"son make money"};//函數進行了重寫
}
int main()
{
Father *father = new Son();
delete father;
return 0;
}
上述代碼在Father類構造函數中調用了虛函數makeMoney(),我們期望運行的結果是怎樣的?(雖然看起來很奇怪…)
不管期望如何,實際的運行結果是這樣的: “father make money”!也就是說此時構造函數中的虛函數makeMoney沒有進行動態綁定。
運行構造函數和析構函數時,對象都是不完整的。同時在基類的構造函數和析構函數中,將派生類對象當成基類對象對待,因此結論就是此時運行的虛函數則是構造析構函數自身類定義的版本函數,無法進行動態綁定。