天天看點

C++中的虛函數簡介(上)1 虛函數的定義2虛函數的使用

繼承(inheritance)是面向對象設計(object-oriented programming)中的一個非常重要的思想,通過繼承,可以定義相似的類型并對其相似關系進行模組化。通過繼承聯系在一起的類構成了一種層次關系。通常在層次關系的根部有一個基類(base class),其它類則直接或間接地從基類繼承而來,這些繼承得到的類稱為派生類(derived class)。

對于基類的函數,派生類可以直接繼承,也可以将基類的函數修改為适合自身的版本。基類将派生類要修改的函數聲明為虛函數(virtual function)。

1 虛函數的定義

1.1 在基類中聲明虛函數

聲明函數時,在函數的傳回值前加入virtual關鍵字,表示該成員函數為虛函數,可以被派生類修改。

class Base
{
public:
   virtual void Base_Func();
}
           

1.2 在基類中定義虛函數

在Base類内部定義虛函數時,和定義普通函數的方法類似。需要注意的時,如果在Base類的外部定義虛函數時,不能使用virtual關鍵字。

void Base::Base_Func()
{
  cout<<”Base::Base_Func().\n”;
}
           

也就是說,虛函數隻是在聲明時使用virtual ,在定義時不能使用 virtual 。

1.3 在派生類中使用虛函數

定義Base類的派生類Derived,如果要在派生類Derived中重寫Base類的虛函數Base_Func(),需要在Derived類中聲明該函數。在派生類中聲明虛函數時,可以加virtual關鍵字,也可以不加。因為該函數在基類中已經聲明為虛函數,則在派生類中也為虛函數。與在“1.2 在基類中定義虛函數”中相同,在派生類的外部定義虛函數時,也不能加virtual關鍵字。

class Derived:public Base
{
public:
void Base_Func();
}
void Derived::Base_Func()
{
   cout<<”Derived::Base_Func.\n”;
}
           

2虛函數的使用

有如下代碼

Base *pbase = new Derived();
pbase->Base_Func();
delete pbase;
           

因為每個派生類對象都包含基類部分,是以可以将基類的指針指向派生類對象。第一行代碼将基類Base 的指針指向了派生類的 Derived 的對象。第二行代碼通過該指針調用 Base_Func() 函數,由于 pbase 指針可能指向基類,也可能指向派生類,是以在程式編譯時,無法确定調用的 Base_Func() 函數時哪個函數,在運作時才能夠确定調用哪個函數,即派生類的 Base_Func() 函數。

是以,此時程式的輸出為

C++中的虛函數簡介(上)1 虛函數的定義2虛函數的使用

圖1 派生類函數的輸出

3 虛函數分析

C++編譯器發現一個類中有被聲明為virtual的函數時,就會為其産生一個虛函數表VTABLE。VTABLE實際上是一個函數指針的數組。一個類隻有一個VTABLE,派生類也有自己的VTABLE,但是派生類的VTABLE與基類的VTABLE有相同的函數排列順序,同名的虛函數被放在兩個數組的相同位置。在建立類的執行個體時,編譯器還會在每個執行個體的記憶體布局中增加一個vptr字段,該字段指向本類的VTABLE。

C++中的虛函數簡介(上)1 虛函數的定義2虛函數的使用

圖2 vptr與VTABLE

在本程式中,可以看到pbase的值和pderived的值如圖3所示。

C++中的虛函數簡介(上)1 虛函數的定義2虛函數的使用
C++中的虛函數簡介(上)1 虛函數的定義2虛函數的使用

圖3 pbase的值和pderived的值

即此時pbase指向0x00e7f6c0的記憶體;而pderived指向0x00e7fa58的記憶體。接下來看一下這兩處記憶體的内容,如圖5和圖6所示。

C++中的虛函數簡介(上)1 虛函數的定義2虛函數的使用

即,此時pbase->vptr的值是0x00b19b34,而pderived->vptr的值是0x00b19b58。兩個記憶體位址即為Base類和Derived類的VTABLE的位址,裡面的内容即為這兩個類虛函數Base_Func()的位址,如圖7所示。

C++中的虛函數簡介(上)1 虛函數的定義2虛函數的使用

圖7 VTABLE内容

即Base::Base_Func()的位址是0x00b11235;而Derived::Base_Func()的值是0x00b110f5。

繼續閱讀