代碼1
class Base
{
public:
Base(int data=https://www.cnblogs.com/erichome/p/10):ma(data){
cout<<"Base()"<<endl;
}
void show(){cout<<"Base Show()"<<endl;}
void show(int){cout<<"Base Show(int)"<<endl;}
~Base(){cout<<"~Base()"<<endl;}
protected:
int ma;
};
class Derive : public Base
{
public:
Derive(int data=https://www.cnblogs.com/erichome/p/20):Base(data),mb(data){
cout<<"Derive()"<<endl;
}
void show(){cout<<"Derive Show()"<<endl;}
~Derive(){cout<<"~Derive()"<<endl;}
private:
int mb;
};
int main(){
Derive d(20);
Base *pb=&d;
pb->show(); //靜態系結(編譯期間系結)
pb->show(100) //靜態系結(編譯期間系結)
cout<<sizeof(Base)<<endl; // 4 位元組
cout<<sizeof(Derive)<<endl; // 8位元組
cout<<typeid(pb).name()<<endl; // clas Base *
cout<<typeid(*pb).name()<<endl; // class Base
return 0;
}
代碼2
class Base
{
public:
Base(int data=https://www.cnblogs.com/erichome/p/10):ma(data){
cout<<"Base()"<<endl;
}
//虛函式
virtual void show(){cout<<"Base Show()"<<endl;}
//虛函式
virtual void show(int){cout<<"Base Show(int)"<<endl;}
~Base(){cout<<"~Base()"<<endl;}
protected:
int ma;
};
class Derive : public Base
{
public:
Derive(int data=https://www.cnblogs.com/erichome/p/20):Base(data),mb(data){
cout<<"Derive()"<<endl;
}
void show(){cout<<"Derive Show()"<<endl;}
~Derive(){cout<<"~Derive()"<<endl;}
private:
int mb;
};
int main(){
Derive d(20);
Base *pb=&d;
pb->show(); //動態系結(運行期間系結)
pb->show(100) //動態系結(運行期間系結) )
cout<<sizeof(Base)<<endl; // 12 位元組
cout<<sizeof(Derive)<<endl; // 16 位元組
cout<<typeid(pb).name()<<endl; //
cout<<typeid(*pb).name()<<endl; //
return 0;
}
//在上面的代碼中,如果一個類中定義了虛函式,編譯器會做什么??
//1:一個類里面定義了虛函式,那么編譯階段,編譯器給這個類會產生一個唯一的vftable虛函式表,
// 虛函式表中主要存盤的內容就是RTTI和虛函式地址,如下圖
//2:當程式運行時,每一張(程式中的可能有很多類都有虛函式,每一個類都會對應一個虛函式表)虛函式表都會加載到記憶體的.rodata區
// 一個類里面定義了虛函式,那么這個類定義的物件,其運行時,在記憶體中開始部分,多存盤一個vfptr虛函式指標,指向相應
// 型別的虛函式表vftable, 一個型別定義的n個物件,指向同一張表
//3:一個類里面虛函式的個數,不影響物件記憶體大小(vfptr),影響的是虛函式表的大小
//4:如果派生類中的方法,和基類繼承來的某個方法,回傳值,函式名,引數串列都相同,而且基類的方法是virtual虛函式
// 那么派生類的這個方法,自動處理成虛函式


在上面代碼2中,Base 中void show()和 void show(int) 為虛函式, 在Derive 中有回傳值,函式名,引數串列都
相同的 show()方法,這個時候 我叫 重寫(或覆寫 即要回傳值,函式名,引數串列 都相同),這個Derive中的void show()方法也會處理成虛函式.所以有
下圖 Derive中的show()方法地址替換掉繼承來的Base的show()函式地址 ,同時我們也可以看到Base中的 void show(int) 并沒有被覆寫

覆寫:基類和派生類的方法,回傳值,函式名以及引數串列都相同,而且基類的方法是虛函式,那么派生類的方法就自動處理成虛函式,他們之前成為覆寫關系.
所以根據上圖,我們說 覆寫 其實是指虛函式表中函式地址的覆寫
代碼2中的這段代碼
Base *pb=&d;
pb->show(); //動態系結(運行期間系結)
pb->show(100) //動態系結(運行期間系結) )
pb->show() pb是Base型別,所以編譯器會去Base中查看void show()函式情況
如果是普通函式,那么靜態系結(編譯期系結),但是發現是 void show()函式是個虛函式就進行動態系結了
同理 pb->show(100)發現Base中的void show(int )也是個虛函式,所以執行動態系結(運行期系結).
但是 這兩個函式在Derive虛函式表中的區別就是,void show()被Derive覆寫掉了,即虛函式表中的地址被覆寫了
而void show(int) 為被覆寫,所以虛函式表中的 地址仍然是Base的void show(int)的地址
----------------------------------------
cout<<sizeof(Base)<<endl; // 4 +8 =12 位元組 Base中又多了一個虛函式表指標
cout<<sizeof(Derive)<<endl; // 8+8 =16 位元組 Derive 多了一個虛函式表指標
------------------------------------------------------
cout<<typeid(pb).name()<<endl; // Base *
cout<<typeid(*pb).name()<<endl; //
關于這句代碼typeid(*pb).name(),即列印指標指向物件的型別, 先看pb是Base型別,再看有沒有虛函式
1:如果沒有虛函式,那么*pb 識別的是編譯時期的型別,即 Base class
2:如果有虛函式,那么 *pb 識別的是運行時的型別,即RTTI型別,RTTI是存盤在虛函式表中,所以要在運行期識別
即 pb->d(vfptr)->Derive vftable -> Class Derive

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/538498.html
標籤:C++
上一篇:<三>關于多載 隱藏 覆寫
