概念
在面向对象编程中,多态指的是一个接口有不同的实现方式。在C++中,多态是重要的概念之一。其中虚函数表的主要作用就是为了实现多态的机制。
C++支持两种多态性:编译时多态性,运行时多态性。
a.编译时多态性:通过重载函数实现
b 运行时多态性:通过虚函数实现。
首先我们讨论下为什么需要多态概念的引入:我们设置很多小动物,猪,牛,羊等等,每个动物都有自己独特的叫声,但每个动物又都有共同的属性,比如睡觉,吃饭等等。于是我们要为每一个动物创建一个call()方法,表示不同的动物在叫,又必须为每一个动物创建eat,sleep方法,然后因为eat,sleep是属于所有动物的共同的属性,写很多遍eat,sleep内容一样的方法是无法忍受的,因此我们引入了抽象的概念,可以把各类动物都抽象为动物,每个动物又可以实现自己特有的属性call,共有属性eat,sleep。这个在C++中是怎么实现的呢?答案就在今天我们要探讨的虚函数中。我们可以创建猪,牛,羊的方法,通过动物*指向不同的实例,这样,在调用牛->吃的时候,就是调用动物这个类中的实现,而调用牛->叫的时候,我们可以直接调用牛这个类的实现。但具体我们应该怎么实现呢?这里,我们引入virtual关键词,即通过虚函数表实现。
下面我们引入一段代码:
代码示例
#include <iostream>
using namespace std;
class animal
{
public:
virtual void say()
{
cout << "i am animal" << endl;
}
virtual void eat()
{
cout << "i am animal, i am eat" << endl;
}
virtual void sleep()
{
cout << "i am animal, i am sleep" << endl;
}
};
class cattle : public animal
{
public:
void say()
{
cout << "i am cattle" << endl;
}
};
class pig : public animal
{
public:
void say()
{
cout << "i am pig" << endl;
}
};
class sheep : public animal
{
public:
void say()
{
cout << "i am sheep" << endl;
}
};
int main()
{
cout << "virtual test" << endl;
animal* c = new cattle;
c->say();
c->sleep();
c->eat();
animal* s = new sheep;
s->say();
animal* p = new pig;
p->say();
getchar();
return 0;
}
输出结果:
根据上述结果,我们分析一下,所有的指针类型都是animal类型,指向了不同的实例,所以对应的调用结果也不一样的。
分析
那具体的虚函数表在系统中是如何实现的呢?
在animal的实例中,保存了一个_vfptr的虚函数表,因此可以根据表中存储的函数指针,定位具体需要调用的函数,如果实例指向cattle,则animal::say()对应的单元格存储的就是cattle::say()的函数指针。
至此,我们明白了多态是通过虚函数表的方式实现的,并且通过上述截图,发现需要中的cattle::say(),animal::eat(),animal::sleep()均是void类型,因此,这里是通过虚函数表保存函数指针的方式实现的。因此,上述调用中,c->say();实际调用的是c->(_vfptr[0])。
对于上述例子,animal类中也有say方法的实现,但是在子类中,被覆盖了,因此在虚表中也被其覆盖了,如果子类中不对say进行实现,则需要中会指向animal::say()。我们也可以将animal的say方法作为纯虚函数进行设定,即
virtual void say() = 0;
这样,每一个子类在继承了父类animal后,都必须有自己的特化实现。