news 2026/10/28 1:32:30

c++进阶之多态

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
c++进阶之多态

一.概念以及分类

运行时(动态)多态:多个不同种类的对象在运行某个相同接口(功能)时,构成的不同结果,达到不同的状态,就叫做动态多态。

eg:


编译时(静态)多态:主要是函数模板或是函数重载,可传不同参数,得到不同结果,进而达到不同状态。由于实参传给形参在编译阶段,所以叫做编译时多态。


由于把编译时归为静态,执行时归为动态,所以才有了动态多态和静态多态之分。

二.虚函数

前面菱形继承中为解决二义性与冗余,在父类继承方式前,也有添加virtual,那这里的virtual和菱形继承里的有什么关系吗?答案是没有。virtual算一鱼两吃,能代表不同意思。

三.定义以及实现

1.定义

2.多态满足的必要条件

①.必须用引用或指针调用虚函数。

②.被调用的函数必须是虚函数,且派生类必须对基类虚函数进行重写。

重写:

基类虚函数和其对应的派生类虚函数的返回值类型,函数名,参数列表(形参类型,个数,顺序)必须完全相同,函数体可不相同。

我将举一下代码的例子,带大家在代码中明白上面段落的含义。但在查看代码前,先对调用虚函数时的传参规则进行明晰,也是对上一章继承篇中赋值兼容转换的一个复习。

赋值兼容转换:

对于指针,也是如此。

Func函数中,ptr这个基类指针形参,既可以传指向基类的指针,也可以传指向派生类的指针(赋值兼容转换)。通过控制指针的指向,进而调用不同类的虚函数,最终实现不同状态。

#include<iostream> using namespace std; class person { public: virtual void Buyticket() { cout << "全价" << endl; }//子类和父类对应虚函数需要返回值,函数名相同,函数内容可不同。 }; class student : public person { public: virtual void Buyticket(){ cout << "半价" << endl; } }; //利用基类指针对不同类虚函数进行调用,以实现多态。 //通过控制传给基类形参的实参类型,进而调用不同类里的虚函数,这里传参还用到了赋值兼容转换,即把派生类赋值给子类 void Func(person* ptr)//用指针调用虚函数 { ptr->Buyticket(); } int main() { person p; student s; Func(&p); Func(&s); return 0; }

注意:1.基类虚函数的virtual不能去掉,去掉以后就无法满足多态了。但在派生类的虚函数里,virtual是能去掉的,虽然不规范,但依旧能满足重写。

2.不同派生类间也能构成多态:

3.特殊语法——析构函数的重写

语法

如果基类,派生类的析构函数名字不同,均带有virtual,那么两个析构函数也能满足多态。

原理

若基类和派生类的析构函数带上了virtual,则系统会将两个析构函数的函数名特殊处理成destructor(函数名就相同了),那么基类和派生类的析构就构成了多态(派生类的析构构成了重写)。

产生该语法的原因(为什么基类析构建议设计为虚函数)

这么设置是为了服务于下面这个情景:

注:delete的底层是ptr->destructor+ operator delete(free)

先做个假设:A,B的析构不满足多态关系。那么delete时,ptr是什么类型的指针,就会去调用什么类的析构。此时,p1,p2全是基类A*类型的指针,就会全去调用A类的析构;但是p2身在曹营心在汉,虽然是A*类的指针,但指向B类的空间,A类的析构释放不掉B类的空间,就会造成空间的泄漏。

那如果满足多态关系,delete时,究竟调用哪个类的析构(destructor),这是由指向空间的类型所决定的。

4.override和final关键字

override

final

5.重载/重写/隐藏的对比

常考,重点!!!!!!!!

注:隐藏也叫重定义。

6.纯虚函数和抽象类

四.多态的原理

1.虚函数表指针

先通过一道老六题引入虚函数表指针这个概念:

注:指针所占大小与其指向内容无关,与系统位数有关。32位下,指针占4字节;64位下,指针占8字节。

被老六肘击的一幕

读完题目,咳,这不是考内存对齐吗?so easy, int4个字节,char1个字节,对齐3个字节,一共8字节,选C。

but.....man,what can I say?!

正解

让我们调用监视窗口:

在b这个对象的构成里,潜藏着一个虚函数表指针,就是我们接下来要探讨的东西。

虚函数表指针

从上图我们可以看出,b的成员还包括一个指针__vfptr,该指针指向一片区域,该区域存储类里所有虚函数的地址。由此,我们可以看出,虚函数表就是指那片区域,是个存放类里所有虚函数地址的数组。而指针__vfptr指向虚函数表,所以它就叫虚函数表指针。

2.多态的原理

借助虚函数表指针这个概念,我们来剖析多态的原理。

多态里生成一种状态的底层原理流程:

注:

杭哥的图,同一个意思:

综上:当传不同对象,经以上流程,生成不同状态,就构成了多态。

3.动态绑定和静态绑定

4.再探虚表——for面试

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/28 1:32:07

2000-2024年 地级市-城乡居民社保相关数据数据(xlsx)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/28 1:31:45

等保2.0定级备案全流程:从定级到差距分析,附6份可套用模板

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/28 1:30:12

wireshark抓包的必学技巧

1.过滤器用好了&#xff0c;信息量大减半! 抓包容易&#xff0c;分析难&#xff0c;一大堆数据包扑面而来&#xff0c;如何迅速找到关键数据? 直接在 Display Filter 输入ip.addr192.168.1.1 只显示与192.168.1.1相关的流量。过滤 HTTP流量:http只看TCP三次握手:tcp.flags.syn…

作者头像 李华
网站建设 2026/10/28 1:24:46

(最新)基于Springboot农场信息管理平台【附源码+文档】

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华