深入浅出之虚函数原理篇(笔记三)
上一節,我們講到了虛函數,那么你知道虛函數是如何做到多態的嗎?
虛函數是通過后期綁定,在執行時間接通過一張虛函數表,間接調用欲綁定的函數。表中的每一個元素都指向虛函數的地址。當然,編譯器也會為類增加一項成員變量,此成員變量是一個指向虛函數表的指針。可用圖解表示如下:
?
注意:
成員函數memfuc()經過編譯之后,形成代碼,然后放在內存中的代碼區,并不是數據段。成員函數都是放在一起,而且由同一個類的所有對象共享。,用sizeof(a)計算的結果是不包括代碼段的。
以下幾點,等下我們一一論證:
1.每一個由此類派生出來的對象,都有一個vptr。當我們通過對象調用虛函數時,實際上是通過vptr找到虛函數表,然后通過虛函數表找到真正的虛函數地址。
2.虛函數表中的內容是根據類中的虛函數聲明順序一個一個填 入函數指針。
3.當派生類繼承了基類后,同時也繼承了虛函數表,當我們在派生類中修改了虛函數時,其對應的虛函數表中所指向的函數地址,就不再是基類的了,而是派生類的了。此點我們用圖解如下:
?
現在我們來分析一下到底是不是這樣的?這一節講解以下面的代碼為例(在上一節的例子基礎上修改而成)
?
Code#include?<iostream.h>
#include?<stdio.h>
#include?<string.h>
class?CEmployee
{
public:
????int?m_name;
public:
????CEmployee(){};
????
????virtual?int?computerPay()
????{
????????printf("這是基類的計算方法");
????????return(2);
????}
????virtual?void?display()
????{
????????printf("職員");
????}
};
class?CWage:public?CEmployee
{
public:
????int?m_wage;
????int?m_hour;
public:
????CWage(){};
????void?set(int?wage,int?hour)
????{
????????m_wage=wage;
????????m_hour=hour;
????}
????virtual????int?computerPay()
????{
????????return?m_wage*m_hour;
????}
????virtual?void?display()
????{
????????printf("時薪職員");
????}
};
class?CSales:public?CWage
{
public:
????int?n_sale;
????int?n_hour;
public:
????CSales(){};
????
????void?set(int?sale,int?hour)
????{
????????n_sale=sale;
????????n_hour=hour;
????}
????virtual????int?computerPay()
????{
????????return?n_sale*n_hour+CWage::computerPay();
????}
????virtual?void?display()
????{
????????printf("銷售員");
????}
};
void?main()
{
????cout<<sizeof(CEmployee)<<endl;
????cout<<sizeof(CWage)<<endl;
????cout<<sizeof(CSales)<<endl;
????CEmployee?a;
????CWage?????b;
????CSales????c;
????b.m_name=1;
????b.m_wage=2;
????b.m_hour=3;
????cout<<b.m_name<<endl;
????cout<<b.m_wage<<endl;
????cout<<b.m_hour<<endl;
????cout<<&b<<endl;
????cout<<&(b.m_name)<<endl;
????cout<<&(b.m_wage)<<endl;
????cout<<&(b.m_hour)<<endl;
????cout<<&c<<endl;
}
運行后的結果如下:
?
我們來分析一下:
1.sizeof(CEmployee)的大小為8,而這個類中只有一個整型是4個字節,那么還有四個字節是誰占了呢,對了,就是指向虛函數表的vptr成員變量。說明確實在類中只要有虛函數,就一定會有vptr這個成員變量。
2.sizeof(CWage)的大小為16,實際上是基類的整型+本類的兩個整型+從基類繼承的vptr也占用了四個字節,剛好說明派生類繼承了虛函數表。
3.看清0x0012ff68這個內存地址,它是類CWage對象的起始地址,這個地址中的內容剛好是vptr,我們跟蹤程序,調試時可查,我把圖取上來大家看一下:
?
4.虛函數表中的內容是根據類中的虛函數聲明順序一個一個填 入函數指針。大家看上圖先是壓入CWag::computerPay,然后再是CWage::display。
5.當派生類繼承了基類后,同時也繼承了虛函數表,當我們在派生類中修改了虛函數時,其對應的虛函數表中所指向的函數地址,就不再是基類的了,而是派生類的了。現在我們看到的是b對象(即CWage),轉到C對象時,會發現虛函數表中的地址都發生變化了。
今天就先寫到這兒,本人才shu學淺,有什么意見或見意,希望能與大家一同探討。
?
轉載于:https://www.cnblogs.com/chuncn/archive/2009/02/27/1399901.html
總結
以上是生活随笔為你收集整理的深入浅出之虚函数原理篇(笔记三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GridView滚动条
- 下一篇: 骗子是这样把1G硬盘变成120G的