c++|函數 C++ 和 Delphi 的函數覆蓋(Override)與重載(overload)
Spacesoft【暗夜狂沙】
在物件導向編程中,當子類繼承了來自基類的函數後,子類有可能需要對其中的一些函數作出與基類不同處理,比如:
class CHuman
{
public:
void SayMyName()//列印出對象的姓名
{
cout << "Hi, I am a human" << endl;
}
};
那麼很明顯,假如他的子類有一個同名、同參數和傳回值(一句話,一摸一樣)的函數SayMyName,它會調用哪個函數呢?比如現在有一個class CMark
class CMark: public CHuman
{
public:
void SayMyName()
{
cout << "Hi, I am mark" << endl;
}
};
那麼我們要問,下面的程式段:
CHuman *pH = new CMark;
if (pH)
pH->SayMyName();
else
cout << "cast error! " << endl;
delete pH;
pH = NULL;
要列印出來的,真的是我們想要的Hi, I am mark 嗎?
不是。它輸出了Hi, I am a human。這很糟糕,當我們指著一個人要他說出自己的名字的時候,他卻告訴我們他“是一個人”,而不是說出自己的名字。出現這樣的問題原因在於,用基類的指標指向公有衍生類別,可以訪問衍生類別從基類中繼承的成員函數。但如果衍生類別中也有同名的函數,則結果仍然是訪問基類的同名函數,而不是衍生類別本身的函數。而事實上,我們希望的是由一個對象的真實類型來決定到底該調用這些同名函數中的哪一個,就是說,這樣的決議是動態(Dynamic)的。或者我們可以說,我們希望當一個對象是子類型時,它的同名函數在子類中的實現覆蓋(override)掉基類的實現。
我們先從C++對這個問題的處理說起。
這是C++中比較典型的多態的例子,C++用虛函數來實現這樣的多態。具體點說,就是使用virtual 關鍵字來將函數說明成虛函數,在上一個例子中就是應該聲明成:
class CHuman
{
public:
virtual void SayMyName()//列印出對象的姓名
{
cout << "Hi, I am a human" << endl;
}
};
這樣,其他的代碼還是那個老樣子,但是我們的CMark 已經知道怎麼說自己的名字了。CMark 的SayMyName()函數是否加了virtual 關鍵字的說明並沒有關係,因為根據C++文法的規定,因為它覆蓋了CHuman 的同名函數,它自己也就成為virtual 的了。至於為什麼一個virtual 關鍵字有那麼神奇的效果呢?C++ FAQ Lite 對此是這樣說明的: 在C++中,“虛成員函數是動態確定的(在運行時)。也就是說,成員函數(在運行時)被動態地選擇,該選擇基於對象的類型,而不是指向該對象的指標/引用的類型”。於是我們的pH就發現自己其實指向的是一個CMark類型的對象,而不是自己的類型所聲明的CHuman,所以它聰明的調用了CMark的SayMyName。
而Delphi 就是用override 關鍵字來說明函數覆蓋的。被覆蓋的函數必須是虛(virtual)的,或者是動態(dynamic)的,也就是說該函數在聲明時應該包含這兩個指示字中的一個,比如:
procedure Draw; virtual;
在需要覆蓋的時候,只需要在子類中用override 指示字重新聲明一下就可以了。
procedure Draw; override;
在文法上來說,聲明為 virtual和 dynamic是等價的。它們的差別在於,前者在實現上對速度進行了最佳化,而後者對代碼大小進行了最佳化。
假如基類和子類都含有同一個函數名和參數,並且在子類中不加override 指示字呢?這在文法上也是正確的。這意味著子類的函數同名實現把基類的實現隱藏(hide)掉了,儘管這二者在衍生類別中都存在。那麼就回到了本文開頭的第一個例子說明的情況:當我們指著一個人要他說出自己的名字的時候,他卻告訴我們他“是一個人”,而不是說出自己的名字。
值得注意的是,與我們在C++ 中常常不加區分的把覆蓋一個函數和重載一個函數通稱為重載不同,在Delphi 中,只有重載(overload) 才是我們平時所說的重載,被重載的函數依然存在,依靠參數來決定到底調用那個實現。當然,當overload掉的函數和基類的函數參數相同時,基類的實現就被hide掉了,就像上面提到的一樣。而覆蓋(override)則是把讓被覆蓋的函數不可見了,確確實實的"覆蓋"掉了,原來的實現就不見了。基於這樣的原因,許多文章甚至一些書都錯誤的把override翻譯成重載,筆者認為並不合適。