今天下午看一个开源项目的源码时候,碰到了虚函数。为此,就去查资料了解一下C++虚函数实现机制。
先上代码,对着代码讲。
#include <iostream>
#include <stdio.h>
using namespace std;
class Animal
{
public:
virtual void eat(); // eat 指定为虚函数
virtual void sleep();
void work(); // work为非虚函数
void run();
};
class Dog: public Animal
{
public:
void eat();
void work();
};
void Animal::eat()
{
cout << "Aminal eats something" << endl;
}
void Animal::sleep()
{
cout << "Animal is sleeping" << endl;
}
void Animal::work()
{
cout << "Animal works hardly" << endl;
}
void Animal::run()
{
cout << "Aminal is running" << endl;
}
void Dog::eat()
{
cout << "Dog eats bone" << endl;
}
void Dog::work()
{
cout << "Dog works hardly" << endl;
}
int main()
{
Animal* ani = new Dog();
ani -> eat();
ani -> sleep();
ani -> work();
return 0;
}
程序中定义两个类:基类Animal和派生类Dog,也即是父类Animal和子类Dog。
该程序的函数被分为四类:
1.eat函数是虚函数,被重新定义
2.sleep函数是虚函数,未被重新定义
3.work函数是普通函数,被重写
4.run函数是普通函数,未被被重写。
编译器处理虚函数的方法是,对每个类添加一个隐藏成员:一个虚函数表(virtual function table, vtbl)。这里将会创建两个虚函数表。虚函数表里面存放的是每个类中虚函数的地址,注意这里,只存放虚函数的地址,不会存放其他成员函数。
程序会使用Dog类型的虚拟地址表,所以,这里,eat函数会执行子类的eat函数,而sleep仍然会执行父类的sleep函数。而work函数会执行指针的类型,也就是Animal类型。
也就是,对于父类指针指向子类对象的情况来说,除了被重新定义的虚函数(eat函数)会执行子类的函数(子类eat函数)之外,其他所有类型的函数都将执行父类函数。
执行结果如下:
Dog eats bone
Animal is sleeping
Animal works hardly
Aminal is running
--------------------------------
Process exited after 0.2431 seconds with return value 0
请按任意键继续. . .
那如果想执行子类Dog的work函数怎么办?
强制类型转换!
将main函数改写如下:
int main()
{
Animal* ani = new Dog();
ani -> eat();
ani -> sleep();
ani -> work();
ani -> run();
cout << endl;
((Dog*) ani) -> eat();
((Dog*) ani) -> sleep();
((Dog*) ani) -> work();
((Dog*) ani) -> run();
return 0;
}
执行结果如下:
Dog eats bone
Animal is sleeping
Animal works hardly
Aminal is running
Dog eats bone
Animal is sleeping
Dog works hardly
Aminal is running
--------------------------------
Process exited after 0.1338 seconds with return value 0
请按任意键继续. . .
这时候,就和普通的继承一摸一样了,哦不是,一模(mu)一样。
参考:
1. C++ Prime Plus 第六版(中文版)
2.JAVA的多态用几句话能直观的解释一下吗? - 程序狗的回答 - 知乎 https://www.zhihu.com/question/30082151/answer/120520568