详细全面讲解C++中重载、隐藏、覆盖的区别

news2025/1/9 12:00:28

文章目录

      • 总结
      • 1、重载
        • 示例代码
        • 特点
        • 1. 模板函数和非模板函数重载
        • 2. 重载示例与调用规则
        • 示例代码
        • 调用规则解释
        • 3. 特殊情况与注意事项
            • 二义性问题
          • 函数特化与重载的交互
      • 2. 函数隐藏(Function Hiding)
        • 概念
        • 示例代码
        • 特点
      • 3. 函数覆盖(重写,Function Overriding)
        • 概念
        • 示例代码
        • 特点

总结

1、重载:在同一个作用域,函数名相同,参数列表不同,与返回值无关
2、隐藏:在基类和派生类之间发生的关系,函数名相同,派生类的函数把基类的函数给隐藏了,只关注函数数名,不管返回值和参数
3、覆盖:覆盖是隐藏的一种特殊情况,派生类和基类的函数,1 函数名相同;2 返回值相同;3 参数列表相同(不包括this指针在内);4 基类函数为虚函数;即为覆盖。

1、重载

  • 函数重载是指在同一个作用域(通常是在同一个类中,也可以是在全局作用域下)内,存在多个同名函数,但它们的参数列表(参数的个数、类型或者顺序)不同。编译器会根据调用时传递的实际参数来决定调用哪个具体的重载函数,函数重载实现了用同一个函数名来执行相似但参数有所不同的操作,提高了代码的可读性和易用性。
    总结来说就是:在同一个作用域,函数名相同,参数列表不同,与返回值无关
示例代码
#include <iostream>

// 重载函数,参数个数不同
int add(int num1, int num2) {
    return num1 + num2;
}

int add(int num1, int num2, int num3) {
    return num1 + num2 + num3;
}

// 重载函数,参数类型不同
double add(double num1, double num2) {
    return num1 + num2;
}

int main() {
    std::cout << add(2, 3) << std::endl;
    std::cout << add(2, 3, 4) << std::endl;
    std::cout << add(2.5, 3.5) << std::endl;
    return 0;
}

在上述代码中,add 函数有多个重载形式,有的重载函数参数个数不同(如两个 int 参数和三个 int 参数的版本),有的重载函数参数类型不同(如 int 参数和 double 参数的版本)。在 main 函数中调用 add 函数时,编译器根据传入的实际参数情况来选择匹配的重载函数进行调用。

特点
  • 作用域相同:重载的函数必须在同一个作用域内定义,比如都在某个类的内部或者都在全局作用域中。
  • 函数名相同:这是重载的关键特征之一,多个函数共享同一个函数名,方便代码的调用和理解,从使用者角度看好像是同一个函数根据不同情况执行不同逻辑。
  • 参数列表有差异:参数个数、类型或者顺序至少有一项不同,而返回类型不同不能作为函数重载的依据(因为仅返回类型不同时,编译器无法仅根据调用情况准确判断该调用哪个函数)。

除了以上常规的重载,还有一些同学认为泛型编程也存在重载,即模板函数和非模板函数的重载,
在C++中,模板函数与非模板函数之间可以存在重载关系,以下是关于它们重载的详细介绍:

1. 模板函数和非模板函数重载
  • 重载机制:重载允许在同一作用域内存在多个同名函数(对于函数重载而言,这些函数的参数列表有所不同,而返回类型不能作为区分重载的唯一依据),编译器会根据调用时实际传入的参数情况来决定调用哪一个具体的函数。模板函数和非模板函数的重载就是利用了这一机制,在合适的场景下,编译器会依据调用参数去选择调用模板函数版本还是非模板函数版本。
2. 重载示例与调用规则
示例代码
#include <iostream>

// 非模板函数
int add(int num1, int num2)
 {
    return num1 + num2;
}

// 模板函数
template<typename T>
T add(T num1, T num2)
 {
    return num1 + num2;
}

int main()
 {
    int result1 = add(3, 5);  // 调用非模板函数 add(int, int)
    double result2 = add(3.5, 2.5);  // 调用模板函数 add<double>(double, double)
    std::cout << "整数相加结果: " << result1 << std::endl;
    std::cout << "浮点数相加结果: " << result2 << std::endl;
    return 0;
}
调用规则解释
  • 精确匹配优先:当进行函数调用时,编译器首先会寻找参数类型与函数参数列表能精确匹配的非模板函数。在上述代码的 add(3, 5) 调用中,非模板函数 add(int, int) 的参数类型刚好和传入的两个整数参数完全匹配,所以编译器优先选择调用这个非模板函数,而不会去考虑模板函数版本,即使模板函数也能够通过实例化(将 T 实例化为 int)来处理这两个整数参数。
  • 模板函数实例化匹配:如果没有找到精确匹配的非模板函数,编译器会尝试对模板函数进行实例化,看能否通过实例化后的模板函数来匹配调用参数。例如在 add(3.5, 2.5) 调用中,不存在参数类型为两个 double 的非模板函数 add,此时编译器会查看模板函数,将模板参数 T 实例化为 double,生成 add(double, double) 这样一个实例化后的函数版本,它能很好地匹配传入的两个 double 类型参数,所以就调用这个实例化后的模板函数版本。
3. 特殊情况与注意事项
二义性问题
  • 当模板函数和非模板函数的参数匹配存在模糊情况时,可能会导致编译错误,出现二义性问题。例如:
#include <iostream>

// 非模板函数
void func(int num)
 {
    std::cout << "非模板函数func(int)" << std::endl;
}

// 模板函数
template<typename T>
void func(T num) 
{
    std::cout << "模板函数func(T)" << std::endl;
}

int main() 
{
    func(5);  // 编译错误,存在二义性,不知道该调用模板函数还是非模板函数
    return 0;
}

在这个例子中,调用 func(5) 时,传入的整数 5 既可以匹配非模板函数 func(int),也可以通过将模板函数的 T 实例化为 int 来匹配模板函数 func(T),编译器无法确定到底该调用哪一个函数,就会报二义性的编译错误。要解决这类问题,可以通过显式指定模板参数(如 func<int>(5) 就会明确调用模板函数版本)或者调整函数的参数类型等方式,使得调用具有明确的匹配对象。这个问题并不是所有的编译器都存在,在VS2022就不存在该问题。

函数特化与重载的交互
  • 函数模板可以进行特化,即针对特定的类型提供专门的模板函数实现。在存在函数特化的情况下,特化版本、模板函数的通用版本以及非模板函数之间的重载关系也需要遵循上述的调用规则。例如:
#include <iostream>

// 非模板函数
void printData(int num)
 {
    std::cout << "非模板函数打印整数: " << num << std::endl;
}

// 模板函数
template<typename T>
void printData(T data) 
{
    std::cout << "模板函数通用版本打印数据: " << data << std::endl;
}

// 模板函数特化,针对char类型
template<>
void printData<char>(char data) 
{
    std::cout << "模板函数特化版本打印字符: " << data << std::endl;
}

int main() 
{
    int num = 10;
    char ch = 'A';
    printData(num);  // 调用非模板函数printData(int)
    printData(ch);  // 调用模板函数特化版本printData<char>(char)
    return 0;
}

在这里,对于 printData 函数,有非模板函数、模板函数通用版本以及针对 char 类型的特化版本。调用 printData(num) 时,根据精确匹配优先原则,会调用非模板函数 printData(int);而调用 printData(ch) 时,由于存在针对 char 类型的特化版本,会优先调用这个特化版本,而不是模板函数的通用版本,同样体现了编译器在选择调用函数时遵循的优先匹配规则。

2. 函数隐藏(Function Hiding)

概念
  • 函数隐藏同样出现在类的继承关系中,是指在派生类中定义了与基类同名的函数(不管参数列表是否相同),此时派生类的函数会隐藏基类中同名的所有函数(包括重载函数),在派生类的作用域内,如果不使用作用域限定符显式指定,就无法访问到基类中被隐藏的同名函数。
    隐藏:在基类和派生类之间发生的关系,函数名相同,派生类的函数把基类的函数给隐藏了,只关注函数数名,不管返回值和参数
示例代码
#include <iostream>

class Base {
public:
    void func() {
        std::cout << "Base类的func函数" << std::endl;
    }

    void func(int num) {
        std::cout << "Base类的func(int)函数" << std::endl;
    }
};

class Derived : public Base {
public:
    void func(double num) {
        std::cout << "Derived类的func(double)函数" << std::endl;
    }
};

int main() {
    Derived derived_obj;
    derived_obj.func(3.0);  // 调用Derived类的func(double)函数

    // 以下代码编译错误,因为Derived类的func函数隐藏了Base类的func函数,
    // 不能直接在Derived类作用域内调用Base类的func函数
    // derived_obj.func();

    // 使用作用域限定符可以访问Base类的func函数
    derived_obj.Base::func();

    return 0;
}

在上述代码中,Derived 类中定义了 func(double num) 函数,它隐藏了 Base 类中的 func 函数和 func(int num) 函数,所以在 Derived 类的作用域内直接调用 func 函数时,编译器会认为是调用 Derived 类自身定义的函数,如果要访问基类中被隐藏的同名函数,需要通过 Base::func() 这样的作用域限定符来明确指定。

特点
  • 存在继承关系:也是基于类的继承场景出现的情况,派生类定义了与基类同名的函数。
  • 同名即隐藏:只要函数名相同就会发生隐藏,与参数列表是否相同无关,而且是隐藏基类中所有同名函数,这一点和重载、覆盖都不同,重载是通过参数差异来区分不同函数,覆盖是严格按照函数签名一致且有虚函数特性来实现的。
  • 作用域相关访问问题:隐藏导致在派生类作用域内,默认情况下无法直接访问基类中被隐藏的同名函数,需要使用作用域限定符来打破这种隐藏效果,才能调用基类的同名函数。

3. 函数覆盖(重写,Function Overriding)

概念
  • 函数覆盖发生在类的继承关系中,是指在派生类中重新定义了基类中的虚函数,并且要求函数签名(函数名、参数列表、返回类型,返回类型如果是指针或引用类型时允许协变)完全一致(除了 const 修饰符,派生类重写函数可以比基类函数多 const 修饰),当通过基类指针或引用调用该函数时,会根据对象的实际类型(是基类对象还是派生类对象)来决定调用基类的函数还是派生类重写后的函数,这是实现多态性的重要机制。

覆盖:覆盖是隐藏的一种特殊情况,派生类和基类的函数,1 函数名相同;2 返回值相同;3 参数列表相同(不包括this指针在内);4 基类函数为虚函数;即为覆盖。
其原理是子类虚函数表的函数地址覆盖了父类虚函数表里函数的指针。
在这里插入图片描述

示例代码
#include <iostream>

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

class Derived : public Base {
public:
    void func() override {
        std::cout << "Derived类的func函数" << std::endl;
    }
};

int main() {
    Base* ptr = new Derived();
    ptr->func();  // 调用Derived类重写后的func函数

    Base base_obj;
    base_obj.func();  // 调用Base类的func函数

    Derived derived_obj;
    derived_obj.func();  // 调用Derived类的func函数

    return 0;
}

在这个例子中,Derived 类重写了 Base 类中的虚函数 func,通过基类指针 ptr 指向派生类对象时,调用 func 函数会执行 Derived 类中重写后的版本,体现了多态性。而直接使用基类对象或者派生类对象调用 func 函数时,则分别调用各自类中定义的函数。

特点
  • 存在继承关系:必须在派生类和基类之间发生,基类中声明了虚函数,派生类对其进行重写。
  • 函数签名要求严格一致:函数名、参数列表、返回类型(遵循协变规则等情况除外)要相同,比如基类函数是 void func(int num),派生类重写的函数也得是 void func(int num),目的是让编译器能准确识别这是重写关系,以便在多态调用时正确执行相应版本的函数。
  • 虚函数特性:基类中的函数必须是虚函数(通过 virtual 关键字修饰),这样编译器才会在运行时根据对象的实际类型来动态决定调用哪个类的函数,实现多态行为。

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

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

相关文章

计算机网络之---物理层设备

什么是物理层设备 物理层设备是指负责数据在物理媒介上传输的硬件设备&#xff0c;它们主要处理数据的转换、信号的传输与接收&#xff0c;而不涉及数据的内容或意义。常见的物理层设备包括网卡、集线器、光纤收发器、调制解调器等。 物理层设备有哪些 1、网卡&#xff08;N…

js状态模式

允许一个对象在其内部状态改变时改变它的行为。 状态模式将对象的状态封装成独立的类&#xff0c;并使它们可以互相转换 // 定义状态接口class State {constructor() {if (this.constructor State) {throw new Error(不能实例化抽象类);}}// 定义状态方法handle(context) {th…

平面坐标转大地坐标(arcgisPro中进行)

1、将需要转换的红线导入arcgisPro中&#xff0c;如下&#xff1a; 2、在地图菜单栏中&#xff0c;选择坐标转换工具&#xff0c;如下&#xff1a; 3、打开坐标转换工具 4、开启捕捉 5、 设置大地坐标显示格式 6、如下&#xff1a; 7、显示如图&#xff1a; 8、再依次添加几个待…

(长期更新)《零基础入门 ArcGIS(ArcScene) 》实验七----城市三维建模与分析(超超超详细!!!)

城市三维建模与分析 三维城市模型已经成为一种非常普遍的地理空间数据资源,成为城市的必需品,对城市能化管理至关重要。语义信息丰富的三维城市模型可以有效实现不同领域数据与IS相信息的高层次集成及互操作,从而在城市规划、环境模拟、应急响应和辅助决策等众多领域公挥作用、…

SpringBootWeb 登录认证(day12)

登录功能 基本信息 请求参数 参数格式&#xff1a;application/json 请求数据样例&#xff1a; 响应数据 参数格式&#xff1a;application/json 响应数据样例&#xff1a; Slf4j RestController public class LoginController {Autowiredpriva…

夯实前端基础之HTML篇

知识点概览 HTML部分 1. DOM和BOM有什么区别&#xff1f; DOM&#xff08;Document Object Model&#xff09; 当网页被加载时&#xff0c;浏览器会创建页面的对象文档模型&#xff0c;HTML DOM 模型被结构化为对象树 用途&#xff1a; 主要用于网页内容的动态修改和交互&…

UI自动化测试保姆级教程--pytest详解(精简易懂)

欢迎来到啊妮莫的学习小屋 别让过去的悲伤&#xff0c;毁掉当下的快乐一《借东西的小人阿莉埃蒂》 简介 pytest是一个用于Python的测试框架, 支持简单的单元测试和复杂的功能测试. 和Python自带的UnitTest框架类似, 但是相比于UnitTest更加简洁, 效率更高. 特点 非常容易上手…

有序数据中插入不确定数据保证数据插入的位置顺序正确排序

解决有序数据中插入不确定数据保证数据插入的位置顺序正确排序 前言 java 数据库中存储自增id 有序的数据&#xff0c; 前端页面基于 id 5和 6 之间新增一条数据&#xff0c;在 id 6 和 7之间新增 2条&#xff0c;或者更复杂的场景&#xff0c;后台接口如何保存数据使得页面数…

基于 Apache Commons Pool 实现的 gRPC 连接池管理类 GrpcChannelPool 性能分析与优化

基于 Apache Commons Pool 实现的 gRPC 连接池管理类 GrpcChannelPool 性能分析与优化 1. 输出关键信息的代码示例 日志记录方法 使用以下代码记录连接池的关键信息&#xff0c;帮助分析连接池的状态和性能瓶颈&#xff1a; import org.apache.commons.pool2.impl.GenericO…

不同方式获取音频时长 - python 实现

DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” -------------------------------------------------------------…

在 C# 中显示动画 GIF 并在运行时更改它们

您可以通过将按钮、图片框、标签或其他控件的Image属性设置为 GIF 文件 来显示动画 GIF 。&#xff08;如果您在窗体的BackgroundImage属性中显示一个&#xff0c;则不会获得动画。&#xff09; 有几种方法可以在运行时更改 GIF。 首先&#xff0c;您可以将 GIF 添加为资源。…

Element-plus、Element-ui之Tree 树形控件回显Bug问题。

需求&#xff1a;提交时&#xff0c;需要把选中状态和半选中状态 的数据id提交。如图所示&#xff1a; 数据回显时&#xff0c;会出现代码如下&#xff1a; <template><el-tree ref"treeRef" :data"data" show-checkbox node-key"id" …

【江协STM32】9-1/2/3 USART串口协议、USART外设、串口发送串口发送+接收

1. 通信接口 通信的目的&#xff1a;将一个设备的数据传送到另一个设备&#xff0c;扩展硬件系统通信协议&#xff1a;制定通信的规则&#xff0c;通信双方按照协议规则进行数据收发全双工&#xff1a;指通信双方能够同时进行双向通信。发送线路和接收线路互不影响&#xff0c…

小程序租赁系统开发的优势与应用前景分析

内容概要 小程序租赁系统是一种新兴的数字化解决方案&#xff0c;旨在为用户提供更加便捷与高效的租赁服务。它通常包括一系列功能&#xff0c;如在线浏览、即时预定、支付功能以及用户反馈机制。这些系统在使用上极为友好&#xff0c;让用户能够轻松选择所需的商品或服务&…

25/1/8 算法笔记<强化学习> GYM环境

前几天花了好多时间在装各个仿真环境上&#xff0c;有V-rep,Pybullet,unity的Ml-agent,很多一大堆&#xff0c;好多好多问题差点逼疯我&#xff0c;可能就pybullet能玩一点&#xff0c;到之后学了机器人我再来玩它&#xff0c;最后的最后&#xff0c;我发现还得是我的gym&#…

学习随记:word2vec中归一化处理的作用

答案来自ai&#xff0c;直接复用为参考&#xff1a; 向量归一化的好处 将向量进行归一化&#xff0c;使其模长为 1&#xff08;即投射到单位圆/单位球上&#xff09;&#xff0c;在许多情况下具有实际意义和计算优势。以下是归一化的主要好处和原因&#xff1a; 1. 提高数值稳…

【C++】B2108 图像模糊处理

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述题目内容输入格式输出格式示例输入&#xff1a;输出&#xff1a; &#x1f4af;题目分析问题拆解 &#x1f4af;我的做法代码实现代码分析 &#x1f4af;老师的做法…

selenium+pyqt5自动化工具总结

说明&#xff1a;本工具是&#xff0c;操作外部google浏览器、selenium是无法操作qt界面中嵌套的浏览器的&#xff0c; 工具在后面 1. 代码结构 pycharm打开的文件下&#xff0c;再写一个子文件&#xff0c;文件导入的时候把子文件名带上 这样就可以在 外层使用命令 pyinst…

经典多模态模型CLIP - 直观且详尽的解释

对比语言-图像预训练&#xff08;CLIP&#xff09;&#xff0c;这是一种创新的多模态建模策略&#xff0c;能够创建视觉和语言的联合表示。CLIP 的效果非常出色&#xff0c;可以用于构建高度特定且性能卓越的分类器&#xff0c;而无需任何训练数据。本文将深入探讨其理论基础&a…

新时期下k8s 网络插件calico 安装

1、k8s master节点初始化完毕以后一直处于notreadey状态&#xff0c;一直怀疑是安装有问题或者是初始化有问题&#xff08;当然&#xff0c;如果真有问题要先解决这些问题&#xff09;&#xff0c;经过不断探索才发现是网络插件没有安装导致的&#xff0c;根据建议安装calico插…