熬过无人问津的日子,才有诗和远方
✨写在前面
书接上文, 今天学习C++多态的知识。作为面向对象三大核心的最后一个,我们要牢记概念并深入理解多态的底层,到后面也会有很多经典的例题供我们巩固练习。话不多说,开始今天的学习!
✨✨ 感兴趣的同学可以订阅一下我的这个《C++入门》专栏喔~✨✨
✨目录
多态是C++面向对象三大特性之一
多态分为两类
- 静态多态: 函数重载 和 运算符重载属于静态多态
- 动态多态: 派生类和虚函数实现运行时多态
静态多态和动态多态区别:
- 静态多态的函数地址早绑定 - 编译阶段确定函数地址
- 动态多态的函数地址晚绑定 - 运行阶段确定函数地址
多态满足条件
- 有继承关系
- 子类重写父类中的虚函数
多态使用
- 父类指针或引用指向子类对象
函数重写和重载的比较
重写: 函数返回值类型 函数名 参数列表 完全一致
重载:与函数返回值无关,函数名需要一致,参数列表中形参的数量,数据类型,顺序不完全相同
下面用具体代码讲解多态:
- class Animal
- {
- public:
- virtual void eat()
- {
- cout << "吃饭饭" << endl;
- }
- };
- class Dog : public Animal
- {
- void eat()
- {
- cout << "啃大骨头" << endl;
- }
- };
- class Cat : public Animal
- {
- void eat()
- {
- cout << "吃小鱼干" << endl;
- }
- };
- void doEat(Animal &animal)
- {
- animal.eat();
- }
- void test01()
- {
- Dog dog;
- doEat(dog);
- Cat cat;
- doEat(cat);
- }
这里把Animal类设为父类,子类Dog和子类Cat都公共继承Animal父类。父类中的eat前加了virtual关键字,那么eat函数就被叫做虚函数,这些就满足了多态的条件。此外,doEat函数的参数列表是父类的引用,那么也就满足了多态使用的父类引用或指针指向子类对象。
这里注意一下父类引用指向子类对象,例如 Animal &animal=dog;此时的animal调用eat方法,出现的是子类重写后的内容。这是因为虽然animal是父类的数据类型,但是实际上还是属于Dog类这个子类,animal的地址和dog的地址相同,调用的方法自然是子类重写的方法。如果不加引用(&),那就变成了Animal animal=dog;那这个时候animal就是纯粹的Animal类了,调用的eat自然就会是父类的eat函数。
如果doEat不加引用,那么运行结果就会是:
virtual实际上是一个指针
Visual Stduio提供了一个开发者命令提示的强大工具,可以让我们看清类的布局等等。
使用方法:
1、找到”工具“并按图示点开命令行里的开发者命令提示
2、打开后是这个页面,自动跳转到需要操作的路径
3、输入 cl /d1 reportSingleClassLayoutAnimal .\多态\多态基本概念.cpp(父类布局)
在这里可以清楚的看到类Animal所占内存空间为4,因为指针占用的内存空间都是4。
vfptr这个指针指向vftable这个表,表里面存放的是函数的地址&Animal::eat,由于函数可以重写,所以前面加上了作用域来表明是哪个类的函数。
4、 输入cl /d1 reportSingleClassLayoutDog .\多态\多态基本概念.cpp(查看Dog类布局)
由于Dog类继承Animal类,所以占用空间也是4,也有指针virtual以及vfptr指针指向vftable表,只是表中的函数ear地址变为了Dog类的函数地址
5、不加virtual关键字
由于没有了指针,所以该类属于空类,默认占用1个字节的空间
通过以上图示我们知道virtual关键字可以做到函数地址的晚绑定,可以在运行时确定运行哪个类的函数,而且virtual是一个指针,他里面含有vfptr指针指向vftable表,表里存放函数地址。
本篇博客希望让大家明白多态的基本使用,以及弄明白虚函数尤其是virtual关键字的含义,以便于接下来我们的多态实战的顺利进行。希望大家可以研究研究开发者命令提示,确实很新奇和强大!