將衍生類別位址賦給基底類別指針,也就是基底類別指標指派生類別對象,也就是我們平常說的多態
但是反過來的時候,必須經過強制型別轉換才可以編譯通過,
下來直接程式碼:
#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();//调用基类的print输出base
pd->Derived::print();//调用派生类的print输出derived
return 0;
}
我想問用pd->print()為什麼呼叫的是基底類別的print
然後為什麼將基底類別的virtual去掉之後,呼叫的便是衍生類別的print
呼叫哪個虛函數是由物件所指的虛擬函數表所決定的,當你new Base()的時候pb所指向的虛函數表中的虛函數是Base的,用(Derived*)強制轉換並沒改變,而非虛函數主要根據指標類型,也就是說pd一開始就是Derived,所以用Derived的函數,成員函數在呼叫時其實是呼叫了this指標的。
使用virtual的意思是這個函數可以被子類別override,使指向子類別的父類別指標不會跟據指標本身的類別來呼叫這個函數,而(透過查虛函數表V-Table)呼叫被子類別override後的函數。
值得一提的是
這樣寫是不正確,至少也是不安全的,不應該將指向父類別物件的父類別指標賦給子類別指標。
此處的pd->print指向的是虛函數表中父類別的print,所以
呼叫的是Base::print
接下來
這是強行在父類別物件上呼叫子類別的函數(或者說以父類別物件為this,強行呼叫虛擬函數表中的子類別print),一般不建議這麼做。
還真是這樣,剛運行了一下,確實很難理解。
我說一下我的思路,不知道對不對,大家可以參考一下:
第一種情況,寫了virtual,如果成員函數加virtual,表示是動態綁定,只有在執行時候才會找到被調函數的位址。但是在哪裡找呢?似乎每個物件的記憶體中都會存在一份虛表(也有說法是每個類別有一份虛表,物件共享),因為你的Derived指標指向的是Base物件的那塊內存,所以系統就去那塊在記憶體的虛表中找print函數,那塊記憶體中虛表中的print函數的位址就是Base類別的print函數的位址,所以呼叫了基底類別的print函數
第二種情況:把virtual給去了,那就不涉及動態綁定,函數的位址是編譯時候決定的。編譯時候函數位址怎麼確定:是透過在原始程式符號表中找出所得。那麼主程式中pd->print(),很明顯,pd是Derived類,pd->print()相當於Derived::print,那編譯器就到Derived類別找嘍,所以編譯期間把pd-> print()中print()的位址被替換成Derived::print的位址,所以就呼叫了子類別的print函數。
不知道你懂我說啥不