多态实现--虚函数与纯虚函数
- C++中实现多态是使用虚函数表的方法实现的。
- 那么具体怎么实现的呢?
举例说明
- 假设有这样一个多态场景:
- 有一个基类动物(animal类),动物里面又有两个派生类:猫(cat类)和狗(dog类)。现在要求动物类有一个共同的方法:叫声(voice成员函数),但猫和狗叫声是不同的(即:它们的叫声实现方法不同)。
- 那么代码怎么写呢?
多态的代码实现
#include <iostream>
using namespace std;
//1、 定义一个纯虚函数
class animal
{
public:
virtual void voice()= 0; //纯虚函数voice
};
//2、 定义猫(cat)狗(dog)类,共同继承自animal,但对voice进行了具体实现
class cat:public animal
{
public:
virtual void voice()
{
cout <<"喵喵喵"<<endl;
}
};
class dog:public animal
{
public:
virtual void voice()
{
cout <<"汪汪汪"<<endl;
}
};
//3、那么下一步就要给猫和狗做一个统一接口了,传参用基类指针。这个接口只有一个功能,就是调用动物叫声voice。
void animal_voice(animal * a)
{
a->voice();
}
//4、 多态写好了,我们来调用一下试试,写个test看看这个多态有没有问题。我们分别定义一个猫狗对象,来调用统一接口,看看它们的叫声是否相同。
void test()
{
cat c;
dog d;
animal_voice(&c);
animal_voice(&d);
}
int main()
{
test();
return 0;
}
验证结果:
喵喵喵 汪汪汪
说明这个多态已实现完成。
多态原理
那么多态的原理是怎么样的呢?为什么这样写代码,就能实现猫和狗叫声不同,它们明明调用的是同一个接口呀?
我们知道C++在编译时为我们做了很多背后的工作。它为cat和dog分别生成了一张虚函数表,将函数地址(也就是函数指针)记录在各自的虚函数表中。如图所示。
- cat的虚函数表:
cat的虚函数表 |
---|
cat的voice函数的地址 |
- dog的虚函数表:
dog的虚函数表 |
---|
dog的voice函数的地址 |
- 这样当我们用接口调用cat和dog的voice函数时,它们会各自在自己的虚函数表里找到自己的voice函数地址,然后根据这个地址来进行调用了。怎么样,多态实现很简单吧?
虚函数与纯虚函数
- 那么animal里的纯虚函数virtual void voice()= 0;只能这样写吗?它可以像下面这样写成虚函数吗?
```
virtual void voice()
{
}
```
答案是可以,但它们是不同的。这里的虚函数是有实现的,只是它的实现方法是空,那么在cat和dog中我们再次实现这个voice就相当于重写(也就是说,我们也可以选择省略(即:不重写)这个voice)。但如果写成纯虚函数,那么我们就必须要在cat和dog中具体实现voice。
也就是这里的虚函数是有这个函数的实现,只是为空;而纯虚函数是这个函数根本还没实现。