c++多态

本文介绍了多态的概念,包括多态的条件(如通过基类指针调用虚函数和虚函数的重写要求),虚函数、重写(包括协变和析构函数的重写)、final和override的特性,以及抽象类的使用和纯虚函数。还详细讲解了虚函数表的作用,动态绑定和静态绑定的区别,以及多态调用的运行机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.多态的概念

完成某个行为是时,不同的对象去完成是会做出不同的状态

多态是在不同的继承关系的类对象,去调用同一函数,下面这个代码就是多态的展现:
一个指针指向子类对象,进行了赋值兼容,一个指针指向父类对象,两个父类指针调用了两个不同的函数。这就是多态的体现。

2.多态的条件

  1. 必须通过基类的指针或者引用虚函数
  2. 被调用的函数必须是虚函数,而且必须要对父类的虚函数进行重写
1.什么是虚函数?

被virtual修饰的是类成员函数成称为虚函数

2.如何构成重写?

子类中有一个和父类完全相同的虚函数(子类和父类的虚函数的返回值,函数名字,函数参数完全相同),就是子类的虚函数重写了父类的虚函数。

注意:在重写子类虚函数时,子类虚函数在不加virtual关键字时,也可以构成重写  (因为父类的虚函数被继承下来在子类依旧保持虚函数属性),但是这种写法不规范,不建议这样使用 

 

3.函数重写的两个例外 

1.协变 

重写虚函数时,子类与父类虚函数返回值类型可以不同,但是父类虚函数返回值是父类对象的的指针或者引用,子类虚函数返回子类对象的指针或者引用,这种情况就是协变。

2.析构函数的重写 

如果父类的析构函数是虚函数,此时子类析构函数只要定义,不管加不加virtual关键字,子类和父类的析构函数构成重写。虽然子类和父类的析构函数函数名不相同,但是编译器对析构函数名进行了特殊处理,编译后析构函数名统一处理成destrutor

4.final 

修饰虚函数,表示该虚函数不能被重写

5.override

检查子类虚函数是否重写了这个虚函数,如果没有重写编译报错

三种概念的对比:

  • 重载:两个函数在同一作用域,函数名相同函数参数不同
  • 重写:两个函数分布在子类和父类两个作用域,函数名/函数参数/返回值相同(或协变),两个函数都是虚函数
  • 重定义(隐藏)两个函数分布在子类和父类两个作用域,函数名相同,两个子类和父类的同名函数不构成重写就是重定义

3.抽象类

在虚函数的声明后面加上“=0”,则这个函数就是纯虚函数,包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化对象。子类对象继承后不能实例化对象,只有重写虚函数,子类对象才能实例化对象。纯虚函数间接子类重写虚函数。

4.多态原理 

1.虚函数表

虚函数表就是一个函数指针数组

可以通过调试看到,aa对象中有_a成员和_vptrf指针,aa对象中的这个指针我们叫做虚函数表指针(v代表virtual,f代表function),一个含有虚函数的类中都有至少一个虚函数表指针,虚函数的地址都要被放在虚函数表中,虚函数表也简称虚表。

上面的代码展现了几个点:

  1. 子类对象bb中也有一个虚表指针,虚表指针一部分是父类继承的虚函数指针和自己新添加或重写的虚函数指针
  2. 子类虚表生成:a.把父类的虚表内容拷贝一份到子类的虚表中 b.如果子类重写了父类中的虚函数,用子类的虚函数去重写父类的虚函数 c.子类如果添加新虚函数,依照声明顺序把虚函数添加到子类虚表的最后
  3. 虚函数表的本质是一个存储虚函数指针的数组,一般情况下这个数组最后放了一个nullptr
  4. 虚函数表通常是放在只读数据段(.rodata)中的。由于虚函数表存储了函数地址,而函数地址是只读的,所以虚函数表通常被放置在只读数据段中。这样做的好处是可以保证虚函数表的内容不会被修改,从而确保了多态性的正确性。不同的编译器可能会有不同的实现方式,所以具体的存放位置可能会有所差异。

  5. 普通调用:编译链接时,符号表找到函数地址。多态调用:运行时,在虚函数表中找到函数的地址进行调用,所以指向父类调用父类的虚函数,指向子类调用子类的虚函数

  6. 虚函数重写也叫做覆盖,重写是语法层的概念

2.动态绑定和静态绑定
  • 静态绑定也叫做前期绑定,在程序编译阶段确定程序的行为,也叫静态多态,比如:函数重载
  • 动态绑定也叫做后期绑定,在程序的运行阶段,根据拿到的类型确定程序的具体行为,调用具体的函数,也叫做动态多态
  • 下面这个代码和反汇编,可以看出多态的运行过程

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值