天天看点

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。

继续阅读