將衍生類別位址賦給基底類別指針,也就是基底類別指標(biāo)指派生類別對象,也就是我們平常說的多態(tài)
但是反過來的時(shí)候,必須經(jīng)過強(qiáng)制型別轉(zhuǎn)換才可以編譯通過,
下來直接程式碼:
#include<iostream>
usingnamespacestd;
class Base
{
public:
virtual void print()
{
cout<<"base"<<endl;
}
};
class Derived:public Base
{
public:
void print()
{
cout<<"derived"<<endl;
}
};
int main(void)
{
Base * pb=newB ase();//定義基類指針
Derived * pd=(Derived*)pb;//賦值給派生類指針
pd->print();//調(diào)用基類的print輸出base
pd->Derived::print();//調(diào)用派生類的print輸出derived
return 0;
}
我想問用pd->print()為什麼呼叫的是基底類別的print
然後為什麼將基底類別的virtual去掉之後,呼叫的便是衍生類別的print
呼叫哪個(gè)虛函數(shù)是由物件所指的虛擬函數(shù)表所決定的,當(dāng)你new Base()的時(shí)候pb所指向的虛函數(shù)表中的虛函數(shù)是Base的,用(Derived*)強(qiáng)制轉(zhuǎn)換並沒改變,而非虛函數(shù)主要根據(jù)指標(biāo)類型,也就是說pd一開始就是Derived,所以用Derived的函數(shù),成員函數(shù)在呼叫時(shí)其實(shí)是呼叫了this指標(biāo)的。
The virtual specifier specifies that a non-static member function is virtual and supports dynamic binding. It may only appear in the decl-specifier-seq of the initial declaration of a non-static member function (i.ic member function . class definition).
Virtual functions are member functions whose behavior can be overridden in derived classes.
使用virtual的意思是這個(gè)函數(shù)可以被子類別override,使指向子類別的父類別指標(biāo)不會跟據(jù)指標(biāo)本身的類別來呼叫這個(gè)函數(shù),而(透過查虛函數(shù)表V-Table)呼叫被子類別override後的函數(shù)。
值得一提的是
Base * pb=new Base();//定義基類指針
Derived * pd=(Derived*)pb;//賦值給派生類指針
這樣寫是不正確,至少也是不安全的,不應(yīng)該將指向父類別物件的父類別指標(biāo)賦給子類別指標(biāo)。
此處的pd->print指向的是虛函數(shù)表中父類別的print,所以
pd->print(); //調(diào)用父類的print,輸出base
呼叫的是Base::print
接下來
pd->Derived::print();
這是強(qiáng)行在父類別物件上呼叫子類別的函數(shù)(或者說以父類別物件為this,強(qiáng)行呼叫虛擬函數(shù)表中的子類別print),一般不建議這麼做。
還真是這樣,剛運(yùn)行了一下,確實(shí)很難理解。
我說一下我的思路,不知道對不對,大家可以參考一下:
第一種情況,寫了virtual,如果成員函數(shù)加virtual,表示是動態(tài)綁定,只有在執(zhí)行時(shí)候才會找到被調(diào)函數(shù)的位址。但是在哪裡找呢?似乎每個(gè)物件的記憶體中都會存在一份虛表(也有說法是每個(gè)類別有一份虛表,物件共享),因?yàn)槟愕腄erived指標(biāo)指向的是Base物件的那塊內(nèi)存,所以系統(tǒng)就去那塊在記憶體的虛表??中找print函數(shù),那塊記憶體中虛表中的print函數(shù)的位址就是Base類別的print函數(shù)的位址,所以呼叫了基底類別的print函數(shù)
第二種情況:把virtual給去了,那就不涉及動態(tài)綁定,函數(shù)的位址是編譯時(shí)候決定的。編譯時(shí)候函數(shù)位址怎麼確定:是透過在原始程式符號表中找出所得。那麼主程式中pd->print(),很明顯,pd是Derived類,pd->print()相當(dāng)於Derived::print,那編譯器就到Derived類別找嘍,所以編譯期間把pd-> print()中print()的位址被替換成Derived::print的位址,所以就呼叫了子類別的print函數(shù)。
不知道你懂我說啥不