函數隱藏
函數隱藏
“隱藏”是指派生類的函數屏蔽了與其同名的基類函數,具體規則:
如果派生類的函數與基類的函數同名,但是參數不同。此時,若基類無virtual關鍵字,基類的函數將被隱藏。(注意別與重載混淆,雖然函數名相同參數不同應稱之為重載,但這裡不能理解為重載,因為派生類和基類不在同一名字空間作用域內。這裡理解為隱藏) 如果派生類的函數與基類的函數同名,但是參數不同。此時,若基類有virtual關鍵字,基類的函數將被隱式繼承到派生類的中。vtable此時派生類vtable中的函數指向基類版本的函數地址。同時這個新的函數版本添加到派生類中,作為派生類的重載版本。但在基類指針實現多態調用函數方法時,這個新的派生類函數版本將會被隱藏。如果派生類的函數與基類的函數同名,不管函數的參數是不是一樣,只要基類函數沒有virtual關鍵字。此時,基類的函數被隱藏。(注意別與覆蓋混淆,這裡理解為隱藏)。如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數有virtual關鍵字。此時,基類的函數不會被“隱藏”。
函數隱藏(屏蔽):如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆);如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有virtual關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)。總結來說:非override的情況下,派生類對象將屏蔽基類同名函數。
VC++深入詳解:函數的覆蓋和隱藏
1.函數的覆蓋
在上一節介紹多態性的時候,我們給出了下面的代碼片段:
例2-19
class animal
{
public:
…
virtual void breathe()
{
cout<<"animal breathe"<
}
};
class fish:public animal
{
public:
void breathe()
{
cout<<"fish bubble"<
}
};
在基類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"<
}
};
class fish:public animal
{
public:
void breathe()
{
cout<<"fish bubble"<
}
};
你看出來這段代碼和例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類的函數隱藏了Base類的函數,Derived類函數不是虛函數(注意和覆蓋相區別)。Derived2類的函數隱藏了Derived類的函數,由於Derived2類的函數與Base類的函數具有同樣的函數名和參數列表,因此Derived2類的函數是一個虛函數,覆蓋了Base類的函數。注意,在Derived2類中,Base類的函數是不可見的,但這並影響fn函數的覆蓋。
當隱藏發生時,如果在派生類的同名函數中想要調用基類的被隱藏函數,可以使用“基類名::函數名(參數)”的語法形式。例如,要在Derived類的方法中調用Base類的方法,可以使用Base::fn()語句。
有的讀者可能會想,我怎樣才能更好地區分覆蓋和隱藏呢?實際上只要記住一點:函數的覆蓋是發生在派生類與基類之間,兩個函數必須完全相同,並且都是虛函數。那麼不屬於這種情況的,就是隱藏了。
最後,我們再給出一個例子,留給讀者思考,代碼如例2-22所示(EX09.CPP)。
例2-22
class Base
{
public:
virtual void xfn(int i)
{
cout<<"Base::xfn(int i)"<
}
void yfn(float f)
{
cout<<"Base::yfn(float f)"<
}
void zfn()
{
cout<<"Base::zfn()"<
}
};
class Derived : public Base
{
public:
void xfn(int i) //覆蓋了基類的xfn函數
{
cout<<"Drived::xfn(int i)"<
}
void yfn(int c) //隱藏了基類的yfn函數
{
目錄