定義
“隱藏”是指派生類的函式禁止了與其同名的基類函數,具體規則:
如果派生類的函式與基類的函式同名,但是參數不同。此時,若基類無virtual關鍵字,基類的函式將被隱藏。(注意別與重載混淆,雖然函式名相同參數不同應稱之為重載,但這裡不能理解為重載,因為派生類和基類不在同一名字空間作用域內。這裡理解為隱藏) 如果派生類的函式與基類的函式同名,但是參數不同。此時,若基類有virtual關鍵字,基類的函式將被隱式繼承到派生類的中。vtable此時派生類vtable中的函式指向基類版本的函式地址。同時這個新的函式版本添加到派生類中,作為派生類的重載版本。但在基類指針實現多態調用函式方法時,這個新的派生類函式版本將會被隱藏。 如果派生類的函式與基類的函式同名,並且參數也相同,但是基類函式沒有virtual關鍵字。此時,基類的函式被隱藏。(注意別與覆蓋混淆,這裡理解為隱藏)。 如果派生類的函式與基類的函式同名,並且參數也相同,但是基類函式有virtual關鍵字。此時,基類的函式不會被“隱藏”。
函式的覆蓋和隱藏的區別舉例
VC++深入詳解:函式的覆蓋和隱藏
1.函式的覆蓋
在上一節介紹多態性的時候,我們給出了下面的代碼片段:
例2-19
class animal
{
public:
…
virtual void breathe()
{
cout<<"animal breathe"<<endl;
}
};
class fish:public animal
{
public:
void breathe()
{
cout<<"fish bubble"<<endl;
}
};
在基類animal的breathe函式前添加了virtual關鍵字,聲明該函式為虛函式。在派生類fish中重寫了breathe函式,我們注意到,fish類的breathe函式和animal類的breathe函式完全一樣,無論函式名,還是參數列表都是一樣的,這稱為函式的覆蓋(override)。構成函式覆蓋的條件為:
n 基類函式必須是虛函式(使用virtual關鍵字進行聲明)。
n 發生覆蓋的兩個函式要分別位於派生類和基類中。
n 函式名稱與參數列表必須完全相同。
由於C++的多態性是通過虛函式來實現的,所以函式的覆蓋總是和多態關聯在一起。在函式覆蓋的情況下,編譯器會在運行時根據對象的實際類型來確定要調用的函式。
2.函式的隱藏
我們再看例2-20的代碼:
例2-20
class animal
{
public:
…
void breathe()
{
cout<<"animal breathe"<<endl;
}
};
class fish:public animal
{
public:
void breathe()
{
cout<<"fish bubble"<<endl;
}
};
你看出來這段代碼和例2-19所示代碼的區別了嗎?在這段代碼中,派生類fish中的breathe函式和基類animal中的breathe 函式也是完全一樣的,不同的是breathe函式不是虛函式,這種情況稱為函式的隱藏。所謂隱藏,是指派生類中具有與基類同名的函式(不考慮參數列表是否相同),從而在派生類中隱藏了基類的同名函式。
初學者很容易把函式的隱藏與函式的覆蓋、重載相混淆,我們看下面兩種函式隱藏的情況:
(1)派生類的函式與基類的函式完全相同(函式名和參數列表都相同),只是基類的函式沒有使用virtual關鍵字。此時基類的函式將被隱藏,而不是覆蓋(請參照上文講述的函式覆蓋進行比較)。
(2)派生類的函式與基類的函式同名,但參數列表不同,在這種情況下,不管基類的函式聲明是否有virtual關鍵字,基類的函式都將被隱藏。注意這種情況與函式重載的區別,重載發生在同一個類中。
下面我們給出一個例子,以幫助讀者更好地理解函式的覆蓋和隱藏,代碼如例2-21所示。
例2-21
class Base
{
public:
virtual void fn();
};
class Derived : public Base
{
public:
void fn(int);
};
class Derived2 : public Derived
{
public:
void fn();
};
在這個例子中,Derived類的fn(int)函式隱藏了Base類的fn()函式,Derived類fn(int)函式不是虛函式(注意和覆蓋相區別)。Derived2類的fn()函式隱藏了Derived類的fn(int)函式,由於Derived2類的fn()函式與Base類的fn ()函式具有同樣的函式名和參數列表,因此Derived2類的fn()函式是一個虛函式,覆蓋了Base類的fn()函式。注意,在Derived2類中,Base類的fn()函式是不可見的,但這並影響fn函式的覆蓋。
當隱藏發生時,如果在派生類的同名函式中想要調用基類的被隱藏函式,可以使用“基類名::函式名(參數)”的語法形式。例如,要在Derived類的fn(int)方法中調用Base類的fn()方法,可以使用Base::fn()語句。
有的讀者可能會想,我怎樣才能更好地區分覆蓋和隱藏呢?實際上只要記住一點:函式的覆蓋是發生在派生類與基類之間,兩個函式必須完全相同,並且都是虛函式。那么不屬於這種情況的,就是隱藏了。
最後,我們再給出一個例子,留給讀者思考,代碼如例2-22所示(EX09.CPP)。
例2-22
#include <iostream.h>
class Base
{
public:
virtual void XFN(int i)
{
cout<<"Base::xfn(int i)"<<endl;
}
void yfn(float f)
{
cout<<"Base::yfn(float f)"<<endl;
}
void zfn()
{
cout<<"Base::zfn()"<<endl;
}
};
class Derived : public Base
{
public:
void xfn(int i) //覆蓋了基類的xfn函式
{
cout<<"Drived::xfn(int i)"<<endl;
}
void yfn(int c) //隱藏了基類的yfn函式
{
cout<<"Drived::yfn(int c)"<<endl;
}
void zfn() //隱藏了基類的zfn函式
{
cout<<"Drived::zfn()"<<endl;
}
};
void main()
{
Derived d;
Base *pB=&d;
Derived *pD=&d;
pB->xfn(5);
pD->xfn(5);
pB->yfn(3.14f);
pD->yfn(3.14f);
pB->zfn();
pD->zfn();
}