【C++】多态,虚函数,重载,重写,重定义,final,override,抽象类,虚函数表,动态绑定,静态绑定详解

news2024/11/20 16:33:51

目录

1. 多态的定义

1.1 多态的构成条件

1.2 虚函数

1.3 虚函数重写

1.4 重载,重写,重定义

1.5 final

1.6 override

2. 抽象类

3. 多态的原理  

3.1 虚函数表

3.2 子类的虚函数表

3.3 多态本质 

3.4 动态绑定和静态绑定

4. 多继承关系的虚表

4.1 单继承的虚表

4.2 多继承的虚表

5. 选择题

5.1

6. 问答题

6.1 

6.2 

6.3

6.4

6.5 


1. 多态的定义

多态就是多种形态。

1.1 多态的构成条件

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。

比如Student继承了 Person。Person对象买票全价,Student对象买票半价。

【条件】

1. 虚函数重写。

2. 必须父类指针或引用去调用虚函数。

1.2 虚函数

virtual修饰的成员函数称为虚函数。

class Person 
{
public:
     virtual void BuyTicket() { cout << "买票-全价" << endl;}
};

1.3 虚函数重写

子类中有一个跟基类完全相同的虚函数(返回值、函数名、参数列表完全相同),然后修改子类的函数体,称子类的虚函数重写了基类的虚函数。

class Person
{
public:
     virtual void BuyTicket() { cout << "买票-全价" << endl; }
};

class Student : public Person 
{
public:
     virtual void BuyTicket() { cout << "买票-半价" << endl; }
}

【两个例外】

1. 协变,(虚函数的返回值不相同)。

只要基类虚函数返回基类对象的指针或引用,派生类虚函数返回派生类对象的指针或引用,或者它们的返回值构成继承关系的引用或指针,也算虚函数重写。

class A{};
class B : public A {};

class Person 
{
public:
     virtual A* f() {return new A;}
};

class Student : public Person 
{
public:
     virtual B* f() {return new B;}
};

2. 析构函数重写,(虚函数的函数名不同)

虽然基类与派生类析构函数名字不同,但是编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。

class Person 
{
public:
     virtual ~Person() {cout << "~Person()" << endl;}
};

class Student : public Person 
{
public:
     virtual ~Student() { cout << "~Student()" << endl; }
};

// 只有派生类Student的析构函数重写了Person的析构函数,下面的delete对象调用析构函
数,才能构成多态,才能保证p1和p2指向的对象正确的调用析构函数。
int main()
{
     Person* p1 = new Person;
     Person* p2 = new Student;
     delete p1; //p1->destructor() + operator delete(p1)
     delete p2; //指向父类调父类,指向子类调子类
     return 0;
}

1.4 重载,重写,重定义

1.5 final

1. final修饰类,类不能被继承。

2. final修饰虚函数,虚函数不能被重写。

class Car
{
public:
     virtual void Drive() final {}
};

class Benz :public Car
{
public:
     virtual void Drive() {cout << "Benz-舒适" << endl;} //报错
};

1.6 override

检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

class Car
{
public:
     virtual void Drive(){}
};

class Benz :public Car 
{
public:
     virtual void Drive() override {cout << "Benz-舒适" << endl;}
};

2. 抽象类

1. 在虚函数的后面写上 =0 ,则这个函数为纯虚函数。

2. 包含纯虚函数的类叫做抽象类(也叫接口类)。

3. 抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生 类才能实例化出对象。间接强制子类虚函数重写,因为你不重写就不能实例化对象。

class Car
{
public:
    virtual void Drive() = 0;
};

class Benz :public Car
{
public:
     virtual void Drive()
     {
         cout << "Benz-舒适" << endl;
     }
};

class BMW :public Car
{
public:
     virtual void Drive()
     {
         cout << "BMW-操控" << endl;
     }
};

【接口继承和实现继承】

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。

虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成 多态,继承的是接口。

3. 多态的原理  

3.1 虚函数表

1. 一个含有虚函数的类中成员变量会多一个指针,这个指针是虚函数表指针,这个指针指向虚函数表,虚函数表存放着虚函数的地址,虚函数表也简称虚表。

2. 对象存的是虚表指针,虚表存的是虚函数指针,虚表和虚函数存在代码段。

3.2 子类的虚函数表

1. 子类由两部分组成,一部分是继承父类,一部分是自己的,继承父类里包含虚表指针,但这个指针和父类不是同一个,指针指向的虚函数表也是先从父类拷贝下来,如果子类有虚函数重写就会用重写后的新函数地址去覆盖原本在虚函数表中的地址。

2. 为什么只能父类的指针或引用,父类对象调用不能多态吗?

因为子类赋值给父类对象相当于子类的父类部分成员拷贝给父类,但是虚函数表指针不会拷贝。

3. 同一个类的对象共用一张虚函数表,父子类是不同的,哪怕没有重写。

4. 派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。

5. 虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后放了nullptr。

3.3 多态本质 

1. 所以,为什么能实现多态,首先你是父类的指针或引用,那么你调父类虚函数就去找父类虚函数地址找到虚函数表然后找对应函数地址,你调子类虚函数就去找子类中父类部分的虚函数地址找虚函数表然后找对应函数地址,但是此时这个函数地址已经变了,因为被重写然后被新地址覆盖了,所以实现了同样的操作却能调不同的函数。本质也就是运行起来进行指定的操作去对应的表里面去找函数地址,又由于地址被改了,所以出现不同的效果。

3.4 动态绑定和静态绑定

1. 普通调用在编译链接时就确定了地址,动态调用运行时去虚表里面找函数地址调用。

2. 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态, 比如:函数重载

3. 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态。

 

4. 多继承关系的虚表

4.1 单继承的虚表

1. 单继承中类里面只有一个虚表,虚表内容拷贝父类的,如果有重写就覆盖,如果自己也有虚函数就加在后面。

4.2 多继承的虚表

1. 当一个子类继承了两个父类时,子类会有两个虚表,因为继承了两个父类,如果有个重写也会覆盖两个。

2. 子类自己的虚函数会加在第一个父类虚表后面。

【细节】

为什么子类func1覆盖了两个父类的func1,但它们的地址不一样?

1. 首先从汇编的角度,它们的目的地是一样的func1,但过程经过多次中转。

2. 这里的func1是子类的,也就是说调用的时候this是子类类型的指针,指向子类的开头,所以用父类指针调用的时候需要偏移回子类开头。

总结:以前是单继承的时候,父类指针和子类指针都是指向子类的开头所以不用偏移,现在有多个父类了,下面的父类指针就需要偏移到最上面,这样传入的this指针才是整个子类,因为多态调用子类的函数需要传入子类的this指针。所以本质是修正指针,所以不能直接调用func1函数,需要一些中转操作修正。 

5. 选择题

5.1

以下程序输出结果是什么

class A
{
public:
    virtual void func(int val = 1){ std::cout<<"A->"<< val <<std::endl;}
    virtual void test(){ func();}
};
   
class B : public A
{
public:
    void func(int val=0){ std::cout<<"B->"<< val <<std::endl; }
};
   
int main(int argc ,char* argv[])
{
    B*p = new B;
    p->test();
    return 0;
}

【解析】

1. 调用test()是正常调用,因为继承了,所以去父类部分调用test()。

2. 因为在父类部分,所以此时的test隐藏的this是A*类型。

3. A*去调用func()触发了多态,虚函数继承了接口(除了函数体的所有部分),重写的是实现(函数体)。

【答案】

B->1

6. 问答题

6.1 

inline函数可以是虚函数吗?

答:可以,如果是普通调用,inline就起作用,如果是多态调用,inline不起作用。

6.2 

静态成员函数可以是虚函数吗?

答:不可以,因为静态成员函数类似于全局函数只不过受类域限制,只有真正的成员函数才能是虚函数。

6.3

构造函数可以是虚函数吗?

答:虚表在编译的时候生成,对象中的虚表指针在初始化列表初始化,虚函数多态调用要到虚表找,此时指针还没初始化。 

6.4

析构函数可以是虚函数吗? 

可以,场景:父类指针new子类对象,delete父类指针。只有构成多态才能正确调用子类析构。

6.5 

普通调用一样快,多态调用虚函数慢,因为要到虚表寻找。 

code-cpp: C++代码 (gitee.com) 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2182859.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

php语法学习

MySQL问题 如果外部mysql与内部mysql冲突&#xff0c;php连接如果已经打开mysql说明他启动的是外部的mysql8&#xff0c;单独点击服务器启动apache就不会冲突。 打开navicat 打开浏览器测试 1.单行和多行注释 2.中文乱码问题 <?php //echo "Hello World 你好&#…

Agr_Reader 1.7.11 极简优美的RSS阅读器,无广告

Agr Reader是一款简洁、优美、符合Material You风格的RSS阅读器。它不仅提供了强大的全文解析功能&#xff0c;默认支持离线阅读&#xff0c;还具备桌面小组件、自定义样式设置等功能。此外&#xff0c;它支持接入FreshRSS、Tiny Tiny RSS等多种RSS服务&#xff0c;并提供沉浸式…

Android studio配置AVD虚拟机

目录 设置虚拟设备参数 安装HAXM 找到HAXM安装包 安装 启动虚拟设备 设置虚拟设备参数 Tools->Devices Manager->Add a new divece一个加号符号的图标->Create Virtual Device 选择尺寸参数&#xff0c;没有合适的话选择New Hardware Profile&#xff0c;调整好…

Spring1

1.Spring系统架构图 (1)核心层 Core Container:核心容器,这个模块是Spring最核心的模块,其他的都需要依赖该模块 (2)AOP层 AOP:面向切面编程,它依赖核心层容器,目的是==在不改变原有代码的前提下对其进行功能增强== Aspects:AOP是思想,Aspects是对AOP思想的具体实现 (3)数据…

深度学习项目----用LSTM模型预测股价(包含LSTM网络简介,代码数据均可下载)

前言 前几天在看论文&#xff0c;打算复现&#xff0c;论文用到了LSTM&#xff0c;故这一篇文章是小编学LSTM模型的学习笔记&#xff1b;LSTM感觉很复杂&#xff0c;但是结合代码构建神经网络&#xff0c;又感觉还行&#xff1b;本次学习的案例数据来源于GitHub&#xff0c;在…

Stm32的bootloader无法使用问题

Stm32的bootloader无法使用问题 用不了一键下载电路 首先简单地对此处涉及的内容进行介绍:如果stm32的BOOT0引脚为低电平时,系统从FLASH中启动,而如果BOOT0引脚为高电平,且BOOT1为低电平时,系统从自举程序(bootloader)中启动. 我在自制照相机设计中加入了ISP一键下载电路,如…

reverse--->恶意代码分析(第一次接触)。

学习笔记。 前言&#xff1a;第一次接触&#xff0c;朋友发给我的。 取自&#xff1a;22年信息安全管理与评估二阶段。 要求&#xff1a; 下载 查壳 32ida打开。 先上微步云沙箱看看&#xff1a; 样本报告-微步在线云沙箱 (threatbook.com)https://s.threatbook.com/repor…

【经典机器学习算法】谱聚类算法及其实现(python)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;深度学习_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. 前…

我的电池_OK2.16.0 实时监控电池状态,让你不再担心电量问题!

我的电池OK是一款专为电池管理设计的应用程序&#xff0c;能够实时查看电池电量、电压、温度等数据。软件支持预警提醒、单位切换等功能&#xff0c;帮助用户更好地管理和监控手机电池使用情况&#xff0c;提升电池寿命。 大小&#xff1a;2.9M 百度网盘&#xff1a;https://p…

当贝播放器 1.5.0 畅享原画,支持阿里网盘、杜比视界和8K播放

当贝播放器TV是一款专为智能电视设计的视频播放器&#xff0c;具有强大的解码能力&#xff0c;支持阿里网盘、百度网盘等网盘资源导入。此外&#xff0c;还支持外部设备导入&#xff0c;并能自动匹配电影海报封面、内容介绍和剧照。 大小&#xff1a;47.3M 百度网盘&#xff1…

vite 快速入门指南

相关链接 演示地址源码地址vite 官网地址 Vite 是什么 Vite 是由 Evan You&#xff08;Vue.js 创始人&#xff09;开发的现代前端构建工具&#xff0c;专为提升开发体验而设计。它通过创新的开发模式和高效的构建流程&#xff0c;极大提高了开发效率&#xff0c;尤其在处理大…

springboot实战学习(10)(ThreadLoacl优化获取用户详细信息接口)(重写拦截器afterCompletion()方法)

接着学习。之前的博客的进度&#xff1a;完成用户模块的注册接口的开发以及注册时的参数合法性校验、也基本完成用户模块的登录接口的主逻辑的基础上、JWT令牌"的组成与使用、完成了"登录认证"&#xff08;生成与验证JWT令牌&#xff09;以及完成获取用户详细信…

【源码部署】vue项目nvm安装(Windows篇)

nvm node version manager&#xff08;node版本管理工具&#xff09; 通过将多个node 版本安装在指定路径&#xff0c;然后通过 nvm 命令切换时&#xff0c;就会切换我们环境变量中 node 命令指定的实际执行的软件路径。 使用场景&#xff1a;比如我们手上同时在做好几个项目&a…

C# HttpClient请求URL重定向后丢失Authorization认证头

搜查官方文档后发现&#xff1a; HttpWebRequest.AllowAutoRedirect Property (System.Net) | Microsoft Learn 微软提供的http类库HttpClient &#xff08;HttpWebRequest\WebClient已不推荐使用&#xff0c;用HttpClient代替&#xff09;有备注提醒&#xff1a;当使用自动重…

B站字幕提取方法

1.获取json文件内容 1.点击F12进入开发者模式&#xff0c;选择网络模块&#xff1b; 2.输入关键字&#xff0c;例如json、ai_subtitle、subtitle等&#xff1b; 3.点击视频下方的字幕功能&#xff0c;开启&#xff1b;再点击响应单元&#xff0c;复制内容&#xff1b; 2.去jso…

好玩的水表电表

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>水表电表</title><style>* {margin:…

视频——教学篇——12——定一个涨粉小目标,如何从0-10万粉?

文章目录 1、粉丝即正义。什么是粉丝价值&#xff1f;粉丝价值粉丝活跃度商业价值 2、找到账号目标和定位3、涨粉的基础是更新频率4、优质少更与良品多更的策略5、有播放却不涨粉&#xff1f;如何提高播放转粉率&#xff1f; 1、粉丝即正义。什么是粉丝价值&#xff1f; 在了解…

CTMO时代下的营销新力量:2+1链动模式AI智能名片商城小程序

在当今这个瞬息万变的商业世界里&#xff0c;营销领域正经历着一场深刻的变革。传统的CMO岗位似乎在时代的浪潮中逐渐失去了它的光芒&#xff0c;CTMO正在悄然取代传统CMO的岗位。 随着营销丛林现象的出现&#xff0c;企业面临着前所未有的挑战。许多企业发现&#xff0c;那些传…

【RockyLinux 9.4】CentOS也可以用。安装教程(使用U盘,避免踩坑简略版本)

一、制作一个镜像安装盘 1.下载镜像&#xff08;本教程使用9.4版本&#xff09; 官网&#xff1a; https://rockylinux.org/zh-CN 2.使用 UltraISO&#xff0c;制作写入硬盘镜像 二、调整相关参数&#xff0c;准备进入安装流程 1.关闭 Secure Boot&#xff08;BIOS 里面关…

【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)

文章目录 从零实现 list 容器&#xff1a;细粒度剖析与代码实现前言1. list 的核心数据结构1.1节点结构分析&#xff1a; 2. 迭代器设计与实现2.1 为什么 list 需要迭代器&#xff1f;2.2 实现一个简单的迭代器2.2.1 迭代器代码实现&#xff1a;2.2.2 解释&#xff1a; 2.3 测试…