当前位置: 首页 > news >正文

【C++入门】虚函数与多态

文章目录

  • 前言
    • 虚函数是什么?
      • 如何使用虚函数?
    • 纯虚函数是什么?
    • 虚函数与普通函数的区别
    • 虚表
      • 虚表是什么?
      • 含有虚表的类内存结构图
      • 如何找到虚表的地址?
      • 示例代码
      • 代码解释
    • 多态是什么?
      • 如何使用多态?
      • 为什么要使用多态?
    • 多态遇到的所有情况
  • 总结


前言

C++ 是一种功能强大的编程语言,以其面向对象编程(OOP)特性而闻名。虚函数和多态是 C++ 面向对象编程中的两个重要概念,它们使得代码更灵活、更具扩展性。本篇文章将介绍虚函数与多态,包括其定义、使用方法及其在实际编程中的作用。


虚函数是什么?

虚函数是一种在基类中使用 virtual 关键字声明的函数,它允许在运行时通过基类指针或引用调用子类的重写版本。这种机制使得 C++ 能够实现多态性。

如何使用虚函数?

在基类中声明虚函数,并在子类中重写该虚函数。基类指针或引用可以指向子类对象,并在运行时调用子类的重写版本。

#include <iostream>class Base {
public:virtual void show() {std::cout << "Base show" << std::endl;}
};class Derived : public Base {
public:void show() override {std::cout << "Derived show" << std::endl;}
};int main() {Base* ptr = new Derived();ptr->show();  // 调用的是 Derived 的 show 方法delete ptr;return 0;
}

纯虚函数是什么?

纯虚函数是一种没有实现的虚函数,必须在派生类中实现。它使用 = 0 语法来声明。声明纯虚函数的类称为抽象类,不能直接实例化。

class AbstractBase {
public:virtual void pureVirtualFunction() = 0; // 纯虚函数
};class ConcreteDerived : public AbstractBase {
public:void pureVirtualFunction() override {std::cout << "ConcreteDerived implementation of pureVirtualFunction" << std::endl;}
};int main() {// AbstractBase obj; // 错误,不能实例化抽象类ConcreteDerived obj;obj.pureVirtualFunction(); // 调用派生类实现的纯虚函数return 0;
}

虚函数与普通函数的区别

  1. 调用时机

    • 普通函数:在编译时确定调用的函数。
    • 虚函数:在运行时确定调用的函数(动态绑定)。
  2. 多态性

    • 普通函数:不支持多态性。
    • 虚函数:支持多态性,可以通过基类指针或引用调用派生类的重写版本。

虚表

虚表是什么?

在 C++ 中,虚表(vtable)是一个由编译器维护的数据结构,用于实现虚函数的动态绑定。当一个类中包含虚函数时,编译器会为这个类创建一个虚表。虚表是一个指针数组,其中每个指针指向该类的虚函数实现。当通过基类指针或引用调用虚函数时,程序会通过虚表找到实际要调用的函数。

每个包含虚函数的类对象都有一个隐藏的指针,称为虚表指针(vptr),它指向该对象所属类的虚表。因此,通过虚表指针,程序可以在运行时动态地确定调用哪个函数。

含有虚表的类内存结构图

有下面这样的两个类:

class Base {
public:virtual void func1();int baseData;
};class Derived : public Base {
public:void func1() override;void func2();int derivedData;
};

它的内存结构图如下

+-----------------+
| Derived object  |
+-----------------+
| vptr ---------->+-------------------------+
| derivedData     |                         |
+-----------------+                         |
| baseData        |                         |
+-----------------+                         ||||+-----------------+   +-----------------+| Virtual Table   |   | Virtual Table   || for Derived     |   | for Base        |+-----------------+   +-----------------+| func1()         |   | func1()         || Derived::func1  |   | Base::func1     |+-----------------+   +-----------------+| func2()         || Derived::func2  |+-----------------+

如何找到虚表的地址?

找到虚表地址的方法涉及使用一些底层的 C++ 技巧和对内存布局的理解。以下是一个简单的示例代码,用于展示如何找到一个类对象的虚表地址并打印虚表中的内容。

示例代码

#include <iostream>class Base {
public:virtual void func1() {std::cout << "Base::func1" << std::endl;}virtual void func2() {std::cout << "Base::func2" << std::endl;}
};class Derived : public Base {
public:void func1() override {std::cout << "Derived::func1" << std::endl;}void func2() override {std::cout << "Derived::func2" << std::endl;}
};typedef void(*FuncPtr)();void printVTable(FuncPtr* vtable) {std::cout << "Virtual Table Address: " << vtable << std::endl;for (int i = 0; vtable[i] != nullptr; ++i) {std::cout << "Function " << i << " address: " << vtable[i] << std::endl;}
}int main() {Derived obj;// 获取虚表指针的地址FuncPtr* vptr = *reinterpret_cast<FuncPtr**>(&obj);printVTable(vptr);return 0;
}

代码解释

  1. 类定义

    • Base 类中定义了两个虚函数 func1func2
    • Derived 类继承自 Base 并重写了这两个虚函数。
  2. 函数指针类型

    • typedef void(*FuncPtr)(); 定义了一个函数指针类型 FuncPtr,用于指向虚函数。
  3. 打印虚表函数

    • printVTable 函数接受一个虚表指针数组并打印虚表地址和其中每个函数的地址。
  4. main 函数

    • 创建一个 Derived 类对象 obj
    • 使用 reinterpret_cast 将对象的地址转换为虚表指针数组的指针,从而获取虚表地址。
    • 调用 printVTable 函数打印虚表内容。

多态是什么?

多态性是面向对象编程的一种特性,它允许通过基类指针或引用在运行时调用不同子类的重写方法。多态性使得代码更灵活和可扩展。

如何使用多态?

通过基类指针或引用指向子类对象,并调用基类中的虚函数。在运行时,根据实际指向的对象类型调用相应的重写方法。

#include <iostream>class Animal {
public:virtual void sound() {std::cout << "Some generic animal sound" << std::endl;}
};class Dog : public Animal {
public:void sound() override {std::cout << "Bark" << std::endl;}
};class Cat : public Animal {
public:void sound() override {std::cout << "Meow" << std::endl;}
};void makeSound(Animal* animal) {animal->sound(); // 根据实际对象类型调用相应的 sound 方法
}int main() {Dog dog;Cat cat;makeSound(&dog); // 输出 BarkmakeSound(&cat); // 输出 Meowreturn 0;
}

为什么要使用多态?

  1. 代码复用:多态性允许通过统一的接口调用不同子类的方法,提高代码的复用性和可维护性。
  2. 灵活性:多态性使得程序在运行时根据实际情况调用不同的方法,提高了代码的灵活性和扩展性。
  3. 可扩展性:可以在不修改已有代码的情况下添加新的子类,并在运行时通过多态性使用它们。

多态遇到的所有情况

  1. 通过基类指针或引用调用子类方法

    • 使用基类指针或引用指向子类对象,调用虚函数实现多态性。
  2. 对象切片问题

    • 如果将子类对象赋值给基类对象,会发生对象切片问题,子类的附加属性和方法会被切掉。
    Base baseObj = derivedObj; // 对象切片,丢失 Derived 部分
    
  3. 多重继承中的多态

    • C++ 支持多重继承,可以通过虚继承解决多重继承中的菱形继承问题,使多态性依然有效。
  4. 虚析构函数

    • 在有多态的情况下,基类通常需要一个虚析构函数,以确保通过基类指针删除派生类对象时,正确调用派生类的析构函数。
    class Base {
    public:virtual ~Base() {std::cout << "Base destructor" << std::endl;}
    };class Derived : public Base {
    public:~Derived() {std::cout << "Derived destructor" << std::endl;}
    };int main() {Base* ptr = new Derived();delete ptr;  // 正确调用 Derived 的析构函数return 0;
    }
    

总结

虚函数和多态是 C++ 中面向对象编程的重要特性,通过虚函数可以实现多态性,使得代码更加灵活和可扩展。虚函数允许在运行时通过基类指针或引用调用子类的重写方法,而纯虚函数则强制派生类实现某些方法,使得抽象类无法实例化。多态性使得程序可以在运行时根据实际对象类型调用相应的方法,提升了代码的复用性、灵活性和可扩展性。然而,在使用多态时,需要注意对象切片问题和虚析构函数的使用,以确保正确的对象管理和方法调用。掌握虚函数和多态的概念和应用,对于提高 C++ 编程水平和开发效率至关重要。

相关文章:

【C++入门】虚函数与多态

文章目录 前言虚函数是什么&#xff1f;如何使用虚函数&#xff1f; 纯虚函数是什么&#xff1f;虚函数与普通函数的区别虚表虚表是什么&#xff1f;含有虚表的类内存结构图如何找到虚表的地址&#xff1f;示例代码代码解释 多态是什么&#xff1f;如何使用多态&#xff1f;为什…...

wpf中轮询显示图片

本文的需求是&#xff0c;在一个文件夹中&#xff0c;放一堆图片的集合&#xff0c;然后在wpf程序中&#xff0c;按照定时的方式&#xff0c;循序显示照片。 全部代码 1.声明一个PictureInfo类 namespace WpfApp1 {public class PictureInfo{public string? FileName { get; …...

CSA笔记9-磁盘管理(2)

分区挂载 挂载&#xff1a;将该文件系统中的内容与指定的目录关联起来&#xff0c;使得你可以通过该目录来访问文件系统中的文件和目录。 mount 命令用来挂载文件系统 #挂载/dev/sda1和/dev/sda2 [rootlocalhost ~]# mkdir test{1..2} [rootlocalhost ~]# ll test1 te…...

Python入门第三课

# 入门第三课 # 关键字 if and or in not in ! car g print(car g) print(car dd) if car ! hh:print("wlcome to here ") age 33 print(age 33) print(age 44) age1 44 if age >0 and age1 > 0:print("nihao") if age >0 or age1 > …...

java计算器,输入公式和对应变量的值

目标&#xff1a;最近想写个东西&#xff0c;本质就是一个计算器&#xff0c;我们可以输入公式&#xff08;例如&#xff1a;ab&#xff09;&#xff0c;然后把公式的值&#xff08;a:10,b:20&#xff09;也输入进去。最后得到结果。核心&#xff1a;这个想法核心部分就是给一个…...

加密货币赋能跨境电商:PayPal供应链金融服务如何引领行业新趋势

跨境电商行业近年来呈现出爆发式增长&#xff0c;随着全球化贸易壁垒的降低和数字经济的快速发展&#xff0c;越来越多的商家和消费者跨越国界进行交易。根据eMarketer的数据&#xff0c;全球跨境电商交易额在2023年已超过4万亿美元&#xff0c;并预计在未来几年内仍将保持两位…...

redis面试(二)List链表数据

list 列表 我们总是说List为列表&#xff0c;其实在真正的数据结构来说&#xff0c;redis是自己基于c语言来实现的双向链表数据结构&#xff0c;主要的逻辑就是每个节点都可以指向下一个节点&#xff0c;这个结构就属于链表数组结构。 每个节点中的属性如下&#xff1a; type…...

SpringDataJPA(三):多表操作,复杂查询

一、Specifications动态查询 有时我们在查询某个实体的时候&#xff0c;给定的条件是不固定的&#xff0c;这时就需要动态构建相应的查询语句&#xff0c;在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象。 import …...

嵌入式硬件面试题集萃:从基础到进阶

基础问题 问题: 解释什么是微控制器&#xff0c;以及它与微处理器的区别。 答案: 微控制器是具有集成内存和输入/输出外设的微型计算机。与通用微处理器相比&#xff0c;微控制器通常用于控制特定应用&#xff0c;而不是执行通用计算任务。 问题: 什么是数字逻辑门&#xff0c…...

easyui-datebox 只显示月份选择,默认开启月份,隐藏日期选择框

如果你使用 ​​easyui-datebox​​​ 并希望隐藏日期选择框,只显示月份选择,可以通过一些自定义代码来实现。虽然 EasyUI 没有直接提供这种功能,但可以通过自定义 ​​formatter​​​ 和 ​​parser​​​ 方法,以及修改 ​​onShowPanel​​ 事件来实现这个功能。 以下…...

【数据结构】队列(链表实现 + 力扣 + 详解 + 数组实现循环队列 )

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;数据结构 &#x1f4da;本系列文章为个人学…...

02 Go语言操作MySQL基础教程_20240729 课程笔记

概述 如果您没有Golang的基础&#xff0c;应该学习如下前置课程。 Golang零基础入门Golang面向对象编程Go Web 基础Go语言开发REST API接口_20240728 基础不好的同学每节课的代码最好配合视频进行阅读和学习&#xff0c;如果基础比较扎实&#xff0c;则阅读本教程巩固一下相…...

相交链表 - 力扣(LeetCode)C语言

160. 相交链表 - 力扣&#xff08;LeetCode&#xff09; (点击前面链接即可查看题目) 一、题目 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始…...

【Python】基础学习技能提升代码样例3:JSON文本处理

对json的处理&#xff0c;无非是编码和解码两部分 编码&#xff1a;将python数据结构转换为json字符串解码: 将json字符串转换为python数据结构 另外&#xff0c;还有.json文件的读写 一、编码 json.dumps(obj, *, skipkeysFalse, ensure_asciiTrue, check_circularTrue, a…...

最新Yiso智云搜索引擎系统源码/开源PHP源码/修复版

源码简介&#xff1a; 最新Yiso智云搜索引擎系统源码/开源PHP源码/修复版。Yiso 是一个性能非常好的搜索引擎&#xff0c;不仅免费开源&#xff0c;还能当作收录网址的平台来用呢&#xff01;只需要输入关键词&#xff0c;就能轻松找到相关的搜索结果内容。 1、Yiso 用的是自…...

Anconda 快速常用命令简洁版

目的&#xff1a;简单清楚的使用基本的conda 命令 可能需求 查看项目中的虚拟环境及依赖是否满足需求操作新环境来满足项目或者论文的实现 Anconda 常用命令 conda 查看基础命令1. 进入Anaconda 环境2. 查看版本3.查看有哪些虚拟环境4.激活虚拟环境5. 进入虚拟环境查看6. 退出…...

Android 系统启动动画

一、接着我们把 bootanimation.zip 动画文件 预制到 /system/media/ 目录下&#xff1a; 二、目录/system/media/bootanimation.zip PRODUCT_COPY_FILES \$(LOCAL_PATH)/bootanimation.zip:/system/media/bootanimation.zipPRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST \ /…...

解决antd打开modal时页面自动跳到顶部问题

问题原因&#xff1a;antd的样式中有一行&#xff0c;如下样式代码&#xff0c;这行代码导致了在本来有滚动条的页面底部触发modal弹出时&#xff0c;会自动滚动到页面顶部。 html {overflow-y: scroll; } 解决办法&#xff1a;删除这行代码、或者将html的overflow-y属性改成…...

什么是等保测评2.0,等保测评如何定级

在信息化时代&#xff0c;网络安全已成为国家安全的重要组成部分。为了应对日益复杂的网络安全形势&#xff0c;我国推出了网络安全等级保护制度&#xff0c;其中等保测评是评估信息系统安全防护能力的关键环节。本文将深入探讨等保2.0的测评流程和定级标准&#xff0c;以揭示其…...

【嵌入式英语教程--6】C语言中的数组与指针

C语言中的数组与指针 英文原文 Arrays and pointers are fundamental concepts in the C programming language. An array is a collection of elements of the same data type stored in contiguous memory locations. Arrays can be used to store and manipulate sequence…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局&#xff1a;刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断"&#xff0c;医生需通过显微镜观察组织切片&#xff0c;在细胞迷宫中捕捉癌变信号。某省病理质控报告显示&#xff0c;基层医院误诊率达12%-15%&#xff0c;专家会诊…...