C++ 多态 虚函数表

news2024/11/26 22:19:22

文章目录

  • 简易抽象理解多态
  • 多态的具体实现
  • 虚函数的定义
  • 虚函数的重写
  • 重定义(隐藏)、重载 、重写(覆盖)区别
  • C++11 override 和 final 关键字
  • 抽象类的定义
  • 接口继承和实现继承
  • 多态的原理:虚函数表
  • 单继承和多继承关系的虚函数表
  • 动态绑定与静态绑定

简易抽象理解多态

不同的对象做同一件事产生了不同的结果。

多态的具体实现

多态实现的条件:

  1. 必须通过基类的指针或者引用调用虚函数
  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

例:

#include <iostream>

class Base {
public:
    virtual void func() {
        std::cout << "Base::func()" << std::endl;
    }
};

class Derived : public Base {
public:
	//注意此处func前虽可以不加virtaul,但为了规范最好加上
    void func() override {
        std::cout << "Derived::func()" << std::endl;
    }
};

int main() 
{
    Base* basePtr_1 = new Derived();
    Base* basePtr_2 = new Base();
    basePtr_1->func();  // "Derived::func()"
    basePtr_2->func();  //  "Base::func()"
    
}

虚函数的定义

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

虚函数的重写

虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。

存在两种特殊的虚函数重写

  1. 协变(基类与派生类虚函数返回值类型不同)
    派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。

  2. 析构函数的重写(基类与派生类析构函数的名字不同)
    如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。

重定义(隐藏)、重载 、重写(覆盖)区别

重定义(又名隐藏) 条件:
1.两函数分别在基类和派生类中。
2.函数名相同,但不构成重写。

重载 条件 :
0.函数名称必须相同。
1.在同一作用域。
2. 函数参数类型、个数或顺序至少有一个不同。
3. 函数返回类型可以相同也可以不同。
4. 函数重载不能仅仅依靠参数的名称或者参数的默认值来区分。
5. 函数重载不能仅仅依靠函数的访问权限或者静态性质来区分。

重写(又名覆盖)条件:
1.两函数分别在基类和派生类。
2. 派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的
返回值类型、函数名字、参数列表完全相同)(由于虚函数表的原因,虚函数表是个函数指针数组)

C++11 override 和 final 关键字

override 和 final 关键字主要用于规范重写。

  1. final:修饰虚函数,表示该虚函数不能再被重写
  2. override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

具体使用方法:在虚函数参数列表后添加关键字即可

抽象类的定义

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。

接口继承和实现继承

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数。(接口包括 函数的返回类型 函数名 形参 )

接口继承是指派生类只继承了基类的接口(也就是纯虚函数),而没有继承基类的实现。这种方式使得派生类必须实现基类中的所有纯虚函数,从而使得派生类和基类的实现是分离的,实现了接口和实现的分离。这种继承方式常常用于实现抽象类和接口,强制要求派生类实现接口中的所有函数。

多态的原理:虚函数表

在C++中,虚函数是一种特殊的成员函数,它可以被子类重写,实现多态性。为了实现虚函数的动态绑定,C++编译器会为每个包含虚函数的类生成一个虚函数表(vtable),也称为虚表。

虚函数表是一个指针数组,其中每个指针指向一个虚函数。每个包含虚函数的类都有一个虚函数表,其中包含该类的所有虚函数的地址。当一个对象被创建时,它会包含一个指向其类的虚函数表的指针。当调用一个虚函数时,编译器会使用该对象的虚函数表来查找正确的函数地址。
关于虚函数表/虚表指针 的相关细节:

1.虚函数表是存储在全局数据区 。
2.虚表指针是指向虚函数表的指针。
3.virtual关键字给的是类中的虚函数,而不是虚表指针。虚表指针是由编译器自动生成的
4.虚表是在编译时由编译器生成的,通常存储在只读数据段中,因此在程序运行时是不允许修改虚表的。
5.满足多态条件以后的函数调用,不是在编译时确定的,是运行起来以后到对象的中取找的(通过 虚表指针-> 虚表 -> 虚函数)。不满足多态的函数调用时编译时确认好的。

单继承和多继承关系的虚函数表

在这里插入图片描述
在这里插入图片描述

动态绑定与静态绑定

  1. 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态,比如:函数重载
  2. 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态。
以下是一个静态绑定的示例代码:
#include <iostream>

class Base {
public:
    void print() {
        std::cout << "Base class." << std::endl;
    }
};

class Derived : public Base {
public:
    void print() {
        std::cout << "Derived class." << std::endl;
    }
};

int main() {
    Base base;
    Derived derived;

    Base* ptr1 = &base;
    ptr1->print();  // 静态绑定,调用Base类的print函数

    Base* ptr2 = &derived;
    ptr2->print();  // 静态绑定,调用Base类的print函数

    return 0;
}

以下是一个动态绑定的示例代码:
#include <iostream>

class Base {
public:
    virtual void print() {
        std::cout << "Base class." << std::endl;
    }
};

class Derived : public Base {
public:
    void print() {
        std::cout << "Derived class." << std::endl;
    }
};

int main() {
    Base base;
    Derived derived;

    Base* ptr1 = &base;
    ptr1->print();  // 动态绑定,调用Base类的print函数

    Base* ptr2 = &derived;
    ptr2->print();  // 动态绑定,调用Derived类的print函数

    return 0;
}

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

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

相关文章

Nginx可视化Nginx-gui

Github&#xff1a;GitHub - onlyGuo/nginx-gui: Nginx GUI Manager 运行方式支持docker、window 下载后压缩&#xff0c;直接运行startup.bat 默认账号密码&#xff1a;admin/admin

Flutter iOS 集成使用 flutter boost

在 Flutter项目中集成完 flutter boost&#xff0c;并且已经使用了 flutter boost进行了路由管理&#xff0c;这时如果需要和iOS混合开发&#xff0c;这时就要到 原生端进行集成。 注意&#xff1a;之前建的项目必须是 Flutter module项目&#xff0c;并且原生项目和flutter m…

CorelDRAW2023矢量制图支持Windows和macOS系统安装使用

CorelDRAW Graphics Suite - 矢量制图专为企业打造的矢量设计作图软件CorelDRAW Graphics Suite套装&#xff0c;支持Windows和macOS系统安装使用。使用 CorelDRAW Graphics Suite 2023&#xff0c;打破创意障碍&#xff0c;以自己的风格进行自由创作。与全球数百万仰赖 CorelD…

睡眠助手/白噪音/助眠夜曲微信小程序源码 附教程

简介&#xff1a; 睡眠助手/白噪音/助眠夜曲微信小程序源码 附教程 支持分享海报 支持暗黑模式 包含了音频数据 最近很火的助眠小程序&#xff0c;前端vue&#xff0c;可以打包H5&#xff0c;APP&#xff0c;小程序 后台可以设置流量主广告&#xff0c;非常不错的源码 代码完…

Linux操作系统知识点总结(二)

总结&#xff08;一&#xff09;链接Linux操作系统知识点总结&#xff08;一&#xff09;&#xff08;附VMware、CentOS以及finalshell的安装教程&#xff09;_你好&#xff0c;明天&#xff0c;&#xff0c;的博客-CSDN博客 43. 由于虚拟机的 Linux系统的IP地址是通过DHCP服务…

前端CSS文字阴影text-shadow记录

前端CSS文字阴影text-shadow记录 一、文字阴影 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Doc…

【基于IDEA + Spark 3.4.1 + sbt 1.9.3 + Spark MLlib 构建逻辑回归鸢尾花分类预测模型】

逻辑回归进行鸢尾花分类的案例 背景说明&#xff1a; 基于IDEA Spark 3.4.1 sbt 1.9.3 Spark MLlib 构建逻辑回归鸢尾花分类预测模型&#xff0c;这是一个分类模型案例&#xff0c;通过该案例&#xff0c;可以快速了解Spark MLlib分类预测模型的使用方法。 依赖 ThisBui…

“算法详解”系列第3卷贪心算法和动态规划出版

“算法详解”系列图书共有4卷&#xff0c;目前1到3卷已经出版。最新出版的是第3卷—贪心算法和动态规划。 算法详解 卷3 贪心算法和动态规划 “算法详解”系列图书共有4卷&#xff0c;本书是第3卷—贪心算法和动态规划。其中贪心算法主要包括调度、最小生成树、集群、哈夫曼编…

vue+vite线上环境地址和开发环境配置方式

vuevite线上环境地址和开发环境配置方式 第一种(放飞自我写法) 说明:后端已解决跨域的情况下配置线上部署访问地址和开发时候地址 java解决跨域代码: Configuration public class WebMvcConfig implements WebMvcConfigurer {Overridepublic void addCorsMappings(CorsRegistr…

程序员编写文档的 10 个技巧

编写好的文档在软件开发领域具有重大意义。文档是概述特定问题陈述、方法、功能、工作流程、架构、挑战和开发过程的书面数据或指令。文档可以让你全面了解解决方案的功能、安装和配置。 文档不仅是为其他人编写的&#xff0c;也是为自己编写的。它让我们自己知道我们以前做过什…

初识redis——分布式系统概念

概念 Redis&#xff08;Remote Dictionary Server )&#xff0c;即远程字典服务&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。 最初redis用来作为消息中间件&#xff0c;现在更多…

Java基础入门篇——IDEA开发第一个入门程序(五)

一、IDEA层级结构分类 IntelliJ IDEA的项目结构主要分为以下几个层级&#xff1a; Project&#xff1a; 项目Module: 模块Package: 包Class&#xff1a; 类 一个项目里面可以有多个模块&#xff0c;一个模块里面又可以有多个包&#xff0c;而每个包又可以存放多个类文件。比…

概念解析 | 生成式与判别式模型在低级图像恢复与点云重建中的角力:一场较量与可能性探索

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:生成式模型与判别式模型在低级图像恢复/点云重建任务中的优劣与特性。 生成式与判别式模型在低级图像恢复与点云重建中的角力:一场较量与可能性探索 1. 背景介绍 机器学习…

吐血整理,手机App测试Monkey测试实战总结(最详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Monkey是什么&…

SpringBoot 依赖管理和自动配置---带你了解什么是版本仲裁

&#x1f600;前言 本篇博文是关于SpringBoot 依赖管理和自动配置&#xff0c;希望能够帮助到您&#x1f60a; &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您…

EtherCAT转EtherCAT网关西门子为什么不支持ethercat

大家好&#xff0c;今天要和大家分享一款神器——捷米JM-ECAT-ECAT通讯网关&#xff01;这款网关有什么厉害的呢&#xff1f;且听我慢慢道来。 首先&#xff0c;JM-ECAT-ECAT是一款自主研发的ETHERCAT从站功能的通讯网关。那什么是ETHERCAT呢&#xff1f;简单来说&#xff0c;…

Android, 笔记+课表的app实现

NoteSchedule: 笔记课表&#xff0c;不同于超表和课程格子等笔记类软件&#xff0c;笔记课表的核心是将课表和笔记进行深度绑定&#xff0c;点击每个课表&#xff0c;就进入到笔记view中&#xff0c;点击其中的item就可以进入到笔记详情&#xff1b; 该应用已上线&#xff0c;…

(电脑维修)电脑忘记密码怎么办(未完)

1.准备一个4gU盘&#xff0c;在微PE官网下载一个不高于2.2版本的微PE&#xff0c;并按照顺序制作一个U盘&#xff08;U盘中的资料记得提前拷贝&#xff0c;后期会被格式化掉&#xff09;

element-ui 表格el-table的列内容溢出省略显示,鼠标移上显示全部和定制样式

1、在对应列加上省略显示show-overflow-tooltip属性&#xff0c;如果加上这属性&#xff0c;鼠标移上还是没效果&#xff0c;要考滤是不是层级的原因&#xff0c;被其他挡住了。 :deep(.el-tooltip){position: relative;z-index:9; } <el-table-column label"用款渠…