【C++ 进阶】Cyber霓虹掩体下的代码拟态——【面向对象编程 之 多态】一文带你搞懂C++面向对象编程中的三要素之一————多态!

⚡ CYBER_PROFILE ⚡
/// SYSTEM READY ///


WARNING \]: DETECTING HIGH ENERGY **🌊 🌉 🌊 心手合一 · 水到渠成** ![分隔符](https://i-blog.csdnimg.cn/direct/60a3de2294e9439abad47378e657b337.gif) |------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------| | **\>\>\> ACCESS TERMINAL \<\<\<** || | [**\[ 🦾 作者主页 \]**](https://blog.csdn.net/fengtinghuqu520?spm=1000.2115.3001.5343) | [**\[ 🔥 C语言核心 \]**](https://blog.csdn.net/fengtinghuqu520/category_12955956.html) | | [**\[ 💾 编程百度 \]**](https://blog.csdn.net/fengtinghuqu520/category_13083835.html) | [**\[ 📡 代码仓库 \]**](https://blog.csdn.net/fengtinghuqu520/article/details/147275999?spm=1001.2014.3001.5502) | --------------------------------------- Running Process: 100% \| Latency: 0ms *** ** * ** *** #### 索引与导读 * [1. 多态的基本概念](#1. 多态的基本概念) * [2. 多态的分类](#2. 多态的分类) * * * [A. 编译时多态(静态多态)](#A. 编译时多态(静态多态)) * [B. 运行时多态(动态多态)](#B. 运行时多态(动态多态)) * [3. 什么是虚函数?](#3. 什么是虚函数?) * [4. 多态的构成条件](#4. 多态的构成条件) * [5. 隐藏](#5. 隐藏) * * [5.1)隐藏的两种基本情况](#5.1)隐藏的两种基本情况) * * [A. 函数同名,但参数不同](#A. 函数同名,但参数不同) * [B. 函数同名,参数也相同,且无 \`virtual\`](#B. 函数同名,参数也相同,且无 `virtual`) * [5.2)隐藏 VS 重写](#5.2)隐藏 VS 重写) * [5.3)代码示例](#5.3)代码示例) * [6. 多态的原理](#6. 多态的原理) * * [6.1)虚函数表指针](#6.1)虚函数表指针) * [6.2)虚函数表 (vtable)](#6.2)虚函数表 (vtable)) * [6.3)动态绑定与静态绑定](#6.3)动态绑定与静态绑定) * * [1. 静态绑定](#1. 静态绑定) * [2. 动态绑定](#2. 动态绑定) * [核心区别:静态类型 vs 动态类型](#核心区别:静态类型 vs 动态类型) * [💻结尾--- 核心连接协议](#💻结尾— 核心连接协议) ## 1. 多态的基本概念 从字面上理解,**多态意为"多种状态"**,即同一个行为在不同的对象上具有不同的表现形式 *** ** * ** *** **想象一下"按回车键"这个动作:** * 如果你正在写文档,按回车是"换行"。 * 如果你正在浏览网页,按回车是"跳转/搜索"。 * 如果你正在玩游戏,按回车可能是"打开聊天框"。 同样的指令(按回车),在不同的场景(对象)下产生了不同的结果。 *** ** * ** *** ## 2. 多态的分类 #### A. 编译时多态(静态多态) 在编译阶段就确定了要调用哪个函数。主要通过 **函数重载** (**Overloading**)实现。 * **例子** :同一个函数名 `add`,你可以定义 `add(int, int)`,也可以定义 `add(double, double)`。编译器会根据你传入的参数类型,自动选择对应的版本 *** ** * ** *** #### B. 运行时多态(动态多态) 这是通常大家讨论"多态"时所指的核心概念。程序在运行期间才根据对象的实际类型来决定调用哪个函数。 * **前提条件** : 1. 必须有继承关系。 2. 子类\*\*重写(Override)\*\*父类的虚函数。 3. 父类指针或引用指向子类对象 *** ** * ** *** ## 3. 什么是虚函数? > [🔗Lucy的空间骇客裂缝:虚函数讲解](https://blog.csdn.net/fengtinghuqu520/article/details/150899286?spm=1011.2415.3001.10575&sharefrom=mp_manage_link) *** ** * ** *** ## 4. 多态的构成条件 多态是一个继承关系下的类对象,去调用同一函数,产生了不同的行为。比如 `Student` 继承了 `Person`。`Person` 对象买票全价,`Student` 对象优惠买票 * 必须是基类的**指针** 或者**引用**调用虚函数 * 被调用的函数必须是虚函数,并且完成了**虚函数重写/覆盖** 。 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/cc15246a287445a79ffe74768a395f6c.png) *** ** * ** *** ## 5. 隐藏 简单来说,隐藏是指:**派生类(子类)中定义了与基类(父类)同名的函数或变量,从而导致基类的成员在子类作用域内被屏蔽** *** ** * ** *** ### 5.1)隐藏的两种基本情况 #### A. 函数同名,但参数不同 无论基类函数是否有 `virtual` 关键字,只要子类定义了同名但参数不同的函数,基类的同名函数就会被隐藏。 * **现象**:你无法通过子类对象直接调用父类的那个版本,编译器会报错,因为它只在子类里找到了匹配的函数名,但参数对不上。 #### B. 函数同名,参数也相同,且无 `virtual` 如果父类函数没有 `virtual` 关键字(即不是虚函数),而子类定义了一个完全一样的函数。 * **现象**:这不是重写(Override),而是隐藏。如果你用子类指针调用,执行子类版本;如果你用父类指针调用,执行的是父类版本 *** ** * ** *** ### 5.2)隐藏 VS 重写 | 特性 | 隐藏(Hiding) | 重写(Override) | |---------------|------------|-------------------| | 范围 | 派生类与基类之间 | 派生类与基类之间 | | 函数名 | 必须相同 | 必须相同 | | 参数列表 | 可以不同 | 必须完全相同 | | `virtual` 关键字 | 基类函数无要求 | 基类函数必须有 `virtual` | | 多态性 | 不支持多态,静态绑定 | 支持多态,动态绑定 | *** ** * ** *** ### 5.3)代码示例 ```cpp #include using namespace std; class Base { public: void fun(int a) { cout << "Base::fun(int)" << endl; } }; class Derived : public Base { public: // 隐藏了 Base::fun(int) // 即使参数类型不同,只要名字一样就会触发隐藏 void fun(string s) { cout << "Derived::fun(string)" << endl; } }; int main() { Derived d; d.fun("hello"); // 正常:调用 Derived::fun // d.fun(10); // 报错!Base::fun(int) 已被隐藏,编译器在 Derived 中找不到接收 int 的 fun d.Base::fun(10); // 正确:必须显式指定作用域才能访问被隐藏的成员 return 0; } ``` *** ** * ** *** ## 6. 多态的原理 *** ** * ** *** ### 6.1)虚函数表指针 **下面代码的运行原理是什么?** ```cpp class Base { public: virtual void Func1() { cout << "Func1()" << endl; } protected: int _b = 1; char _ch = 'x'; }; int main() { Base b; cout << sizeof(b) << endl; return 0; } ``` 因为**类 `Base`** 中包含了一个**虚函数 `Func1()`** ,编译器会为这个类生成一张**虚函数表 `(vtable)`** * 为了支持多态,每一个 `Base` 类的对象头部都会多出一个隐藏的指针,叫做 **虚函数表指针** (`_vptr`)。 * 这个指针指向虚函数表的起始地址。 * **大小** : 在 32 位环境下指针占 `4` 字节,在 64 位环境下占 `8` 字节 *** ** * ** *** **我们来看看类中的数据成员:** 1. `int _b`:占 **4 字节**。 2. `char _ch`:占 **1 字节**。 *** ** * ** *** **计算过程(以 32 位环境为例):** 1. `vptr`:`4` 字节(偏移量 `0`-`3`) 2. `_b`:`4` 字节(偏移量 `4`-`7`) 3. `_ch`:`1` 字节(偏移量 `8`) 4. **结构体整体对齐** :为了提高 CPU 读取内存的效率,类的大小必须是其最宽基本类型成员(这里是指针或 `int`,均为 `4` 字节)大小的整数倍。 * 目前总大小是 (4 + 4 + 1 = 9) 字节。 * 向上补齐到 `4` 的倍数,结果为 **12 字节**。 *** ** * ** *** **计算过程(以 64 位环境为例):** 1. `vptr`:`8` 字节(偏移量 `0`-`7`) 2. `_b`:`4` 字节(偏移量 `8`-`11`) 3. `_ch`:`1` 字节(偏移量 `12`) 4. **结构体整体对齐** :此时最宽基本类型是指针(`8` 字节)。 * 目前总大小是 (8 + 4 + 1 = 13) 字节。 * 向上补齐到 `8` 的倍数,结果为 **16 字节**。 *** ** * ** *** ### 6.2)虚函数表 (vtable) * 编译器会为每个包含虚函数的类创建一个数组,这个数组里存放的是该类所有虚函数的地址 * 如果子类重写了父类的虚函数,子类的虚表中对应的函数地址就会被替换成子类自己的函数地址;如果没有重写,则保留父类的地址 ```cpp class Animal { public: virtual void Speak() { cout << "Animal Speak"; } virtual void Eat() { cout << "Animal Eat"; } }; class Dog : public Animal { public: void Speak() override { cout << "Dog Bark"; } // 重写了 Speak // Eat 没重写,直接继承 }; ``` 在内存中: 1. **`Animal`** 的虚表:存放的是 `&Animal::Speak` 和 `&Animal::Eat`。 2. **`Dog`** 的虚表:存放的是 `&Dog::Speak`(因为重写了)和 `&Animal::Eat`(因为没重写,直接用父类的)。 当程序运行到这一行时: ```cpp Animal* a = new Dog(); ``` 对象 `a` 的内存头部有一个 `vptr`,它指向的是 `Dog` 的虚表 *** ** * ** *** ### 6.3)动态绑定与静态绑定 在`C++`这种编译型语言中,**绑定** 指的是**将函数调用与具体的函数代码块连接起来的过程** #### 1. 静态绑定 静态绑定也叫"早绑定" (Early Binding)。 * **发生时间** :在**编译阶段** 。编译器根据代码中变量的**声明类型**(静态类型)来决定调用哪个函数。 * **适用场景**: * 普通函数调用。 * 函数重载(Overloading)。 * 运算符重载。 * 模板(Templates)。 * **特点**: * **效率高**:编译时就确定了地址,运行时直接跳转,没有任何额外开销。 * **缺乏灵活性**:无法根据运行时的实际情况改变行为。 *** ** * ** *** #### 2. 动态绑定 动态绑定也叫"晚绑定" (Late Binding)。 * **发生时间** :在**程序运行阶段** 。程序根据指针或引用所指向的**实际对象类型**来决定调用哪个函数。 * **适用场景**: * 虚函数(Virtual Functions)。 * 当通过父类指针或引用调用虚函数时。 * **特点**: * **灵活性极高**:它是实现多态的基础,允许程序在不修改代码的情况下扩展功能。 * **轻微性能损失**:因为需要查虚函数表(vtable),比直接跳转函数地址要慢一点点(在现代硬件上几乎可以忽略不计)。 *** ** * ** *** #### 核心区别:静态类型 vs 动态类型 ```cpp Animal* a = new Dog(); ``` *** ** * ** *** ## 💻结尾--- 核心连接协议 **警告:** 🌠🌠正在接入底层技术矩阵。如果你已成功破解学习中的逻辑断层,请执行以下指令序列以同步数据:🌠🌠 *** ** * ** *** **【📡】 建立深度链接:** **关注**本终端。在赛博丛林中深耕底层架构,从原始代码到进阶协议,同步见证每一次系统升级。 **【⚡】 能量过载分发:** 执行**点赞**操作。通过高带宽分发,让优质模组在信息流中高亮显示,赋予知识跨维度的传播力。 **【💾】 离线缓存核心:** 将本页加入**收藏**。把这些高频实战逻辑存入你的离线存储器,在遭遇系统崩溃或需要离线检索时,实现瞬时读取。 **【💬】 协议加密解密:** 在**评论区**留下你的散列码。分享你曾遭遇的代码冲突或系统漏洞(那些年踩过的坑),通过交互式编译共同绕过技术陷阱。 **【🛰️】 信号频率投票:** 通过**投票**发射你的选择。你的每一次点击都在重新定义矩阵的进化方向,决定下一个被全量拆解的技术节点。 *** ** * ** *** ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/57b03915c54b43a7a03fa92dbbfe57c3.gif) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0905dc972de8414bb602715de3f866ee.gif)

相关推荐
伴我与影2 小时前
【记录】复现论文 Dftpav
c++·docker
Hical_W3 小时前
深入学习CPP17_PMR
c++·学习
xiaoshuaishuai83 小时前
C# 实现百度搜索算法逆向
开发语言·windows·c#·dubbo
yuan199973 小时前
使用模糊逻辑算法进行路径规划(MATLAB实现)
开发语言·算法·matlab
计算机安禾3 小时前
【数据结构与算法】第42篇:并查集(Disjoint Set Union)
c语言·数据结构·c++·算法·链表·排序算法·深度优先
蒸汽求职4 小时前
北美求职身份过渡:Day 1 CPT 的合规红线与安全入职指南
开发语言·人工智能·安全·pdf·github·开源协议
YuanDaima20484 小时前
二分查找基础原理与题目说明
开发语言·数据结构·人工智能·笔记·python·算法
fox_lht4 小时前
7.3.结构体-方法
开发语言·后端·rust
chenbin___4 小时前
检查hooks依赖的工具(转自千问)
开发语言·前端·javascript·react native·react.js