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

C++虚函数:解锁多态的“动态密码

C++虚函数:解锁多态的“动态密码”


开篇小故事:遥控器的“智能按钮”

假设你有一个万能遥控器,上面只有一个“开关”按钮:

  • 按下时,电视会开机,空调会制冷,电灯会亮起。
  • 同一个按钮,却能根据设备类型触发不同行为。

C++中的虚函数(Virtual Function) 就像这个“智能按钮”,允许基类指针在运行时动态调用子类的具体实现。今天,我们就来揭开虚函数的神秘面纱!


一、虚函数是什么?

虚函数是C++实现运行时多态的核心机制,通过在基类中用 virtual 关键字声明,允许子类重写(Override)该函数。

class Device {
public:virtual void turnOn() {  // 虚函数cout << "设备启动" << endl;}
};class TV : public Device {
public:void turnOn() override {  // 重写虚函数cout << "电视开机,播放欢迎画面" << endl;}
};class AC : public Device {
public:void turnOn() override {cout << "空调开始制冷" << endl;}
};// 使用基类指针调用不同子类的方法
Device* device1 = new TV();
Device* device2 = new AC();
device1->turnOn();  // 输出电视开机
device2->turnOn();  // 输出空调制冷

二、虚函数的工作原理:虚函数表(vtable)

1. 虚函数表:每个类的“功能菜单”
  • 每个包含虚函数的类都有一个虚函数表(vtable),表中存储了该类所有虚函数的地址。
  • 类的每个对象内部隐藏一个指向vtable的指针(vptr)。
2. 动态绑定:运行时“查表调用”

当通过基类指针调用虚函数时:

  1. 通过对象的vptr找到类的vtable。
  2. 从vtable中查找函数地址并执行。
3. 示例内存布局
class Base {
public:virtual void func1() {}virtual void func2() {}int data;
};Base obj;
  • obj 的内存结构:
    | vptr | data |  
    ↑指向Base的vtable(存储func1和func2的地址)
    

三、虚函数的“黄金法则”

1. 虚析构函数

基类的析构函数必须声明为虚函数,否则通过基类指针删除子类对象时,只会调用基类析构函数,导致子类资源泄漏!

class Base {
public:virtual ~Base() {  // 虚析构函数cout << "释放Base资源" << endl;}
};class Derived : public Base {
public:~Derived() override {cout << "释放Derived资源" << endl;}
};Base* obj = new Derived();
delete obj;  
// 输出:
// 释放Derived资源
// 释放Base资源
2. 纯虚函数与抽象类
  • 纯虚函数:没有实现的虚函数,用 = 0 标记。
  • 抽象类:包含纯虚函数的类,不能实例化,只能作为接口。
class Shape {  // 抽象类
public:virtual double area() const = 0;  // 纯虚函数
};class Circle : public Shape {
public:double area() const override { return 3.14 * radius * radius;}
private:double radius;
};

四、虚函数的“高级技巧”

1. 使用override明确重写(C++11)
class Derived : public Base {
public:void func() override {  // 显式声明重写// ...}
};
  • 避免拼写错误或参数不匹配导致的意外隐藏基类函数。
2. 使用final禁止重写(C++11)
class Base {
public:virtual void lock() final {}  // 禁止子类重写
};class Derived : public Base {
public:void lock() override {}  // 编译错误!
};
3. 性能考虑
  • 虚函数调用成本:比普通函数多一次指针解引用和查表操作,但对现代CPU影响甚微。
  • 适用场景:在需要多态的地方使用,避免滥用。

五、虚函数的“常见陷阱”

1. 构造函数中调用虚函数

在构造函数中,虚函数机制未完全建立,调用虚函数会执行基类版本:

class Base {
public:Base() { init(); }virtual void init() { cout << "Base init" << endl; }
};class Derived : public Base {
public:void init() override { cout << "Derived init" << endl; }
};Derived d;  // 输出"Base init",而非"Derived init"
2. 切片问题(对象拷贝)

将子类对象赋值给基类对象时,会发生“切片”,丢失子类特有数据:

Derived d;
Base b = d;  // 仅复制Base部分,Derived部分被“切掉”

六、虚函数的最佳实践

  1. 基类析构函数必须为虚函数
  2. 优先使用纯虚函数定义接口,明确子类职责。
  3. 避免在构造函数/析构函数中调用虚函数
  4. 多态场景下使用指针或引用,而非对象拷贝。

总结:虚函数——C++多态的“灵魂”

虚函数通过动态绑定机制,让代码灵活适应不同类型的对象,是面向对象设计的核心工具。

  • 像设计师一样规划类层次:用抽象类定义接口,用虚函数实现多态。
  • 像工程师一样谨慎:注意虚析构函数、避免切片和构造函数陷阱。

下次当你写下 virtual 关键字时,不妨想象自己正在为代码注入“智能基因”——让程序在运行时自主选择最佳行为!

(完)


希望这篇博客能帮助读者深入理解虚函数的精髓!如果需要补充示例或调整内容,请随时告诉我~ 😊

相关文章:

C++虚函数:解锁多态的“动态密码

C虚函数&#xff1a;解锁多态的“动态密码” 开篇小故事&#xff1a;遥控器的“智能按钮” 假设你有一个万能遥控器&#xff0c;上面只有一个“开关”按钮&#xff1a; 按下时&#xff0c;电视会开机&#xff0c;空调会制冷&#xff0c;电灯会亮起。同一个按钮&#xff0c;却…...

【深度学习】计算机视觉(CV)-目标检测-Faster R-CNN —— 高精度目标检测算法

1.什么是 Faster R-CNN&#xff1f; Faster R-CNN&#xff08;Region-based Convolutional Neural Network&#xff09; 是 目标检测&#xff08;Object Detection&#xff09; 领域的一种 双阶段&#xff08;Two-Stage&#xff09; 深度学习方法&#xff0c;由 Ross Girshick…...

Blazor-父子组件传递任意参数

在我们从父组件传参数给子组件时&#xff0c;可以通过子组件定义的[Parameter]特性的公开属性进行传值&#xff0c;但是当我们需要传递多个值的时候&#xff0c;就需要通过[Parameter]特性定义多个属性&#xff0c;有没有更简便的方式&#xff1f; 我们可以使用定义 IDictionar…...

【原创】vue-element-admin-plus完成编辑页面中嵌套列表功能

前言 vue-element-admin-plus对于复杂业务的支持程度确实不怎么样&#xff0c;我这里就遇到了编辑页面中还要嵌套列表的真实案例&#xff0c;比如字典&#xff0c;主字典嵌套子信息&#xff0c;类似于一个树状结构。目前vue-element-admin-plus给出的例子是无法满足这个需求的…...

【深度学习】计算机视觉(CV)-目标检测-DETR(DEtection TRansformer)—— 基于 Transformer 的端到端目标检测

1.什么是 DETR&#xff1f; DETR&#xff08;DEtection TRansformer&#xff09; 是 Facebook AI&#xff08;FAIR&#xff09;于 2020 年提出的 端到端目标检测算法&#xff0c;它基于 Transformer 架构&#xff0c;消除了 Faster R-CNN、YOLO 等方法中的 候选框&#xff08;…...

DeepSeek教unity------MessagePack-02

内置支持类型&#xff1a; 对象序列化 MessagePack for C# 可以序列化你自己定义的公共类或结构体类型。默认情况下&#xff0c;可序列化的类型必须用 [MessagePackObject] 属性进行注解&#xff0c;成员需要用 [Key] 属性进行注解。键可以是索引&#xff08;整数&#xff09;…...

【达梦数据库】disql工具参数绑定

前言 在达梦数据库的使用过程中尽管管理工具很好用&#xff0c;但是命令行工具还是有着得天独厚的优势&#xff0c;但是在参数绑定方面就没有管理工具做的更加完美&#xff0c;现在就汇总下disql 工具参数绑定的常用几种方式 disql 参数绑定 使用 ? select * from v$dm_in…...

H5应用抓包及调试技巧

由于图片和格式解析问题&#xff0c;可前往 阅读原文 在现代移动互联网时代&#xff0c;H5 应用以其跨平台、轻量化、快速迭代的特性&#xff0c;成为移动开发的重要一环。然而&#xff0c;随着功能的复杂化和用户体验要求的提升&#xff0c;H5应用的调试也面临着诸多挑战&…...

Django后台新建管理员

在 Django 中&#xff0c;新建管理员用户通常涉及使用 Django 自带的命令行工具 manage.py。以下是具体步骤&#xff1a; 前提条件 Django 项目已创建&#xff1a;确保你已经创建了一个 Django 项目和应用。数据库已迁移&#xff1a;确保你已经运行了 python manage.py migra…...

输入网址到网页显示,发生了什么?

从今天起&#xff0c;我准备在网上输出自己的八股了 浏览器解析URL&#xff1a; 根据URL解析 请求协议&#xff08;http&#xff09;&#xff0c;请求的服务器&#xff08;www.baidu.com&#xff09;&#xff0c;请求的文件路径&#xff08;可以省略&#xff09;&#xff0c;解…...

Coredump-N:sprintf写越界

最近遇到一个sanitizer检查出来的问题; unsigned long abc = 0xffffffffffffffff; char link[8] = {0}; sprintf(link, "%u", abc);这段代码存在潜在问题。 数据类型不匹配: abc 是一个 unsigned long 类型...

自学Java-面向对象高级(final、单例类、枚举类、抽象类、接口)

自学Java-面向对象高级&#xff08;final、单例类、枚举类、抽象类、接口&#xff09; 一、final关键字1、认识final关键字2、final修饰变量的注意3、常量 二、单例类&#xff08;设计模式&#xff09;1、设计模式的概念2、单例设计模式3、单例类有很多形式4、懒汉式单例类5、小…...

[LeetCode力扣hot100]-二叉树相关手撕题

简单 94.中序遍历 就说左子树-根-右子树顺序&#xff0c;之前也有二叉树相关的文章&#xff0c;基本上递归为主&#xff0c;这里用栈等方式实现下。 用到&#xff1a;栈 注意上面给出节点的基本结构&#xff0c;如左右&#xff0c;val指等 /*** Definition for a binary t…...

docker下部署kong+consul+konga 报错问题处理

前言&#xff1a; 由于在docker下部署一些项目比较特殊&#xff0c;特别是网络这一块&#xff0c;如果没有搞清楚&#xff0c;是很容易出问题的。 先上docker-compose 编排 这里的docker-compose for kong可以在 kong-compose 获取代码 version: 3.9x-kong-config:&kong…...

网络优化工作流程

DT路测 移动测试&#xff08;Drive Test&#xff09; CQT 定点测试&#xff08;通信质量测试&#xff09; DT 测试不能体现实际话务质量&#xff1a;回音、串音等网络问题不能通过 DT 测试发现&#xff0c;因此 CQT 拨打测试是 DT 测试很好的补充&#xff0c;也是目前室内外测…...

[题解]2024CCPC重庆站-小 C 的神秘图形

Sources&#xff1a;K - 小 C 的神秘图形Abstract&#xff1a;给定正整数 n ( 1 ≤ n ≤ 1 0 5 ) n(1\le n\le 10^5) n(1≤n≤105)&#xff0c;三进制字符串 n 1 , n 2 ( ∣ n 1 ∣ ∣ n 2 ∣ n ) n_1,n_2(|n_1||n_2|n) n1​,n2​(∣n1​∣∣n2​∣n)&#xff0c;按如下方法…...

React入门 - 0.React简介

React入门 - React简介 A Brief Introduction to React By JacksonML 1. 关于React React是一个知名的Web框架。众所周知&#xff0c;jQuery, Angular, Vue等框架都曾闪亮登场&#xff0c;并且&#xff0c;都仍然在全球市场占有一席之地。React这个颇有担当的新锐&#xff0…...

hive全量迁移脚本

#!/bin/bash #场景&#xff1a;数据在同一库下&#xff0c;并且hive是内部表&#xff08;前缀的hdfs地址是相同的&#xff09;#1.读取一个文件&#xff0c;获取表名#echo "时间$dt_jian_2-------------------------" >> /home/hadoop/qianyi_zengliang/rs.txt#…...

SpringCloud系列教程:微服务的未来(二十四)Direct交换机、Topic交换机、声明队列交换机

前言 在现代消息队列系统中&#xff0c;交换机是实现消息传递和路由的核心组件。本文将重点探讨三种常见的交换机类型&#xff1a;Direct交换机、Topic交换机和声明队列交换机。通过对这三种交换机的详细分析&#xff0c;我们将学习它们的工作原理、应用场景以及如何在实际项目…...

Sojson高级加密技术科普

1. 引言 什么是Sojson&#xff1f; Sojson是一款用于JavaScript代码加密与混淆的工具&#xff0c;它能够有效保护前端代码的知识产权&#xff0c;避免开发者的心血被随意窃取。 为什么需要代码加密&#xff1f; 在当今的互联网环境下&#xff0c;代码被轻易复制、篡改或逆向…...

mysql多主集群 galera cluster for mysql 8安装配置启动重启集群

[TOC] 一、安装mysql 1、安装 系统环境&#xff1a; Ubuntu 18.04 64位 MySQL 8.0.19 下载MySQL APT安装配置包 首先访问 https://dev.mysql.com/downloads/repo/apt/ 获取配置包下载地址 wget https://dev.mysql.com/get/mysql-apt-config_0.8.14-1_all.deb sudo dpkg -i mysq…...

gitte远程仓库修改后,本地没有更新,本地与远程仓库不一致

问题 &#xff1a;gitte远程仓库修改后&#xff0c;本地没有更新&#xff0c;本地与远程仓库不一致 现象&#xff1a; [cxqiZwz9fjj2ssnshikw14avaZ rpc]$ git push Username for https://gitee.com: beihangya Password for https://beihangyagitee.com: To https://gitee.c…...

个人搭建CDN加速服务 特网科技

在互联网快速发展的今天&#xff0c;网站的加载速度对用户体验有着至关重要的影响&#xff0c;传统的网页加载方式依赖于服务器的性能和网络环境&#xff0c;这使得某些网站的页面加载时间过长&#xff0c;用户体验不佳&#xff0c;为了解决这个问题&#xff0c;许多企业开始采…...

mybatis 入门案例

前言 我们清楚 mybatis 是一个持久层框架&#xff0c;可以非常便捷的操作数据库。如最常见的对数据进行增删改查操作。 项目准备 1 在mybatis 数据库 创建 user 用户表 并插入以下两条数据 以下是一个user.sql 脚本文件如何使用 脚本文件可以参照MySQL数据库的备份与还原_控…...

Spring Boot最新技术特性深度解析与实战应用

一、反应式编程:WebFlux与非阻塞架构 1.1 核心价值与场景 Spring Boot 2.x全面拥抱反应式编程模型,通过Spring WebFlux支持异步非阻塞的请求处理,适用于高并发、低延迟的微服务场景(如实时通信、物联网数据处理)。其基于Reactor库实现,采用事件循环模型,显著提升资源利…...

Python使用Flask结合DeepSeek开发

一、背景 我之前关于DeepSeek使用ollama部署的文章大家可以把DeepSeek大模型部署起来。那么ollama还提供了可以调用对应部署模型的API接口。我们可以基于这些接口&#xff0c;做自己的二次开发。使用pythonflaskollama就可以进行模型对话调用。并且前端采用SSE的技术&#xff0…...

前端常见面试题-2025

vue4.0 Vue.js 4.0 是在 2021 年 9 月发布。Vue.js 4.0 是 Vue.js 的一个重要版本&#xff0c;引入了许多新特性和改进&#xff0c;旨在提升开发者的体验和性能。以下是一些关键的更新和新特性&#xff1a; Composition API 重构&#xff1a;Vue 3 引入了 Composition API 作为…...

大模型开发实战篇7:语音识别-语音转文字

语音识别大模型&#xff0c;是人工智能领域的一项重要技术&#xff0c;它能够将人类的语音转换为文本。近年来&#xff0c;随着深度学习技术的不断发展&#xff0c;语音识别大模型取得了显著的进展&#xff0c;并在各个领域得到了广泛应用。 主流语音识别大模型 目前&#xf…...

基于JAVA开发APISIX插件实战(1)-开发、部署、调试

我这篇APISIX-JAVA插件实战开编讲述初级工程师才会关心的开发部署调试,主要是因为APISIX-JAVA插件从前文基于JAVA开发APISIX插件-CSDN博客中我们可以了解到,它是通过unix的sock进行RPC的通信的,因此无法生成sock的windows环境是无法进行开发的。 如果使用Mac那么会怎么样?…...

QML 部件获得焦点触发的全局槽函数 onActiveFocusItemChanged

在qml的window窗口中&#xff0c;假如添加里许多其他部件&#xff0c;当这些部件改变时&#xff0c;会有一个全局部件焦点改变槽函数触发&#xff0c;就是 onActiveFocusItemChanged 可以通过此槽函数就可以知道当前焦点在哪一个部件上&#xff0c;也可以做一些自动化测试等&…...