虚函数实现原理
今天学C++多态时,对虚函数实现原理有些疑惑。
然后爬了很多文,看了很多教程。
经过一系列研究,以下是我的理解:
多态是C++面向对象三大特性之一
多态分为两类
- 静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名
- 动态多态: 派生类和虚函数实现运行时多态
静态多态和动态多态区别:
- 静态多态的函数地址早绑定 - 编译阶段确定函数地址
- 动态多态的函数地址晚绑定 - 运行阶段确定函数地址
注意:请按照编号顺序查看代码说明
#include<iostream>
#include<string>
using namespace std;
//动物类
class Animal
{
public:
virtual void speak()
{
// 1.原理:
//没写virtual时 这是一个非静态的成员函数
//它不属于类上面,是分开存储的
//现在这个类是空类,空类的大小是1字节
//但如果加了virtual后,类大小为4字节,变成虚函数
//这个类的结构发生了变化,因为里面会添加一个4字节大小的指针
//叫vfptr(virtual function pointer 虚函数(表)指针)
//这个指针会指向一个虚函数表
//vftable(virtual function table 虚函数表)
//虚函数指针 &对象名
//虚函数表 *(&对象名) - 第一个虚函数的地址
//虚函数表 **(&对象名) - 第一个虚函数
//所有的虚函数都是通过 虚函数指针 在虚函数表中调用的
//虚函数表会记录这个类中所有的虚函数的地址,
//可以理解为 它是一个 只存放这个类的虚函数的地址表
//不管你在这个类中写多少个虚函数,都不会影响这个实例的大小
//依旧是4字节,因为虚函数也是分开存储的
//小知识:在C++的标准说明书中说到
//编译器必须要保证 虚函数表的指针 存在于对象的实例中
//最前面的位置
//也就是那个对象最开始的地址
//(这是为了保证正确取到虚函数的偏移量)
cout << "动物在说话" << endl;
}
};
//猫类
class Cat : public Animal
{
public:
// 2.
// 重写函数:函数返回值类型,函数名,形参列表 要完全相同 才叫重写
// 这里写不写 virtual都可以
// 如果父类的是虚函数的话 重写时 不写的virtual话 也会默认是virtual
void speak() {cout << "小猫在说话" << endl}
};
//狗类
class Dog : public Animal
{
public:
void speak() {cout << "小狗在说话" << endl;}
};
// 5.
// 动态多态满足条件:
// 1.有继承关系
// 2.子类要重写父类的虚函数
//(重写:函数返回值类型,函数名,形参列表 要完全相同 才叫重写)
// 动态多态使用:
// 父类的指针或者引用 执行子类对象
// 4.
void doSpeak(Animal& animal) // Animal & animal = cat;
//C++中允许父子之间的类型转换,不需要做强制类型转换
{
//引用的本质是起别名,引用里有一个常量指针指向cat
//引用本质:Animal* const animal = &cat;
//所以当用animal这个引用调用speak函数时,实际上是:
//引用通过Cat的首地址找到虚函数表,从虚函数表通过指针偏移量获取speak函数的地址,从而调用speak函数
//因为speak的作用域再Cat下面,所以会调用Cat下的speak
animal.speak();
// 因为我们这个函数的地址是早绑定,所以输出的是“动物在说话”
}
void test01()
{
Cat cat;
// 3.
//已知创建子类时会创建一个父类
//父类中虚函数表内部记录了虚函数的地址:&Animal::speak
//然后继承给子类
//子类就拥有了所有父类属性,此时子类中虚函数表的地址依然还是 &Animal::speak
//但是!!
//我们重写了子类函数(此时重写的函数默认是vitural,所以vitural写不写都可以)
//这时虚函数指针 指向的虚函数表 因为重写 就覆盖&Animal::speak 变成 &Cat::speak 这个地址
doSpeak(cat);
Dog dog;
doSpeak(dog);
}
void test02() {cout << "sizeof Animal = " << sizeof(Animal) << endl;}
int main()
{
test01();
test02();
system("pause");
return 0;
}
如果文章有误,欢迎指正: 2892870137@qq.com
欢迎拜访我的主博客:https://h-james.github.io/