函数重载讲解

虽然在初识C++-CSDN博客中介绍过,但还是感觉要单发出来大概讲解下

什么是函数重载?

函数重载是指在同一个作用域内,函数名相同,但它们的 参数列表 不同。C++ 允许你根据函数的参数个数、类型或者顺序的不同来定义多个同名函数。编译器会根据函数调用时提供的参数自动选择最合适的函数版本。

为什么需要函数重载?

函数重载的主要目的是为了让代码更加 简洁可读。你可能会有不同的需求,在这些需求下你需要执行类似的操作(例如打印、加法、计算等)。通过函数重载,你不需要为每个需求创建一个新的函数名,而是通过相同的函数名来处理不同的情况。

函数重载的规则

  1. 函数名必须相同:这是函数重载的基础,必须要有相同的函数名。
  2. 参数列表必须不同:这可以是参数的个数、类型或者顺序不同。返回类型不参与重载的判断。
  3. 返回类型不影响重载:不能仅通过返回类型来区分重载函数。

 举点例子

1. 假设我们有一个 print 函数,目的是输出信息。根据不同的信息,我们可能传入不同数量的参数。例如,打印一个整数,或者打印一个整数和一个字符串:

#include <iostream>
using namespace std;void print(int a) {cout << "Integer: " << a << endl;
}void print(int a, string b) {cout << "Integer: " << a << " and String: " << b << endl;
}int main() {print(5);             // 调用 print(int)print(10, "Hello");   // 调用 print(int, string)return 0;
}

测试结果 

  • 第一个 print(5) 调用了只有一个参数的 print(int a) 版本。
  • 第二个 print(10, "Hello") 调用了接收 intstring 两个参数的 print(int a, string b) 版本。

 这说明

 编译器根据传入的参数数量来选择合适的函数版本。

2. 如果想根据不同的数据类型来输出不同的信息,可以通过类型来重载函数。比如,print 函数可以根据参数类型不同,打印整数、浮点数或者字符串:

#include <iostream>
using namespace std;void print(int a) {cout << "Integer: " << a << endl;
}void print(double a) {cout << "Double: " << a << endl;
}void print(string a) {cout << "String: " << a << endl;
}int main() {print(10);          // 调用 print(int)print(3.14);        // 调用 print(double)print("Hello");     // 调用 print(string)return 0;
}

 测试结果

  • print(10) 调用了 print(int a),因为 10 是整数。
  • print(3.14) 调用了 print(double a),因为 3.14 是浮动数。
  • print("Hello") 调用了 print(string a),因为 "Hello" 是字符串。

结论

 编译器会根据传入参数的数据类型来选择合适的重载函数。

3. 如果函数的参数顺序不同,也可以进行重载。例如,假设你有两个函数,分别接受两个参数 intdouble,但它们的顺序不同:

#include <iostream>
using namespace std;void print(int a, double b) {cout << "Int and Double: " << a << ", " << b << endl;
}void print(double a, int b) {cout << "Double and Int: " << a << ", " << b << endl;
}int main() {print(10, 3.14);    // 调用 print(int, double)print(3.14, 10);    // 调用 print(double, int)return 0;
}

测试结果

  • print(10, 3.14) 调用了 print(int a, double b),因为第一个参数是整数,第二个是浮动数。
  • print(3.14, 10) 调用了 print(double a, int b),因为第一个参数是浮动数,第二个是整数。

结论

编译器根据参数的顺序来判断调用哪个函数版本。 

重载函数的限制

  1. 返回类型不参与重载判断 你不能仅仅通过改变返回类型来进行函数重载。例如,下面的代码是错误的:

    int add(int a, int b) { return a + b; }
    double add(int a, int b) { return a + b; }  // 错误:仅通过返回类型不能区分
    

    这会导致编译错误,因为编译器无法通过仅返回类型的不同来决定到底应该调用哪个函数版本。

  2. 重载的歧义问题 如果传入的参数能够匹配多个重载版本,编译器会发生歧义错误。例如,以下代码就会出错:

    void print(int a) { cout << "Integer: " << a << endl; }
    void print(double a) { cout << "Double: " << a << endl; }print(10);   // 明确调用 print(int)
    print(10.5); // 明确调用 print(double)
    print(10.0); // 可能有歧义,编译器无法确定是否调用 int 或 double
    

介绍完了基础,进入拓展环节  

重载函数的匹配规则

通过上面三个例子我们了解了C++ 编译器选择合适的重载函数时,会根据参数的类型、个数以及顺序来匹配。但匹配的规则可不止这点。

1. 精确匹配

当一个重载函数的参数类型完全匹配时,编译器会选择它。例如:

void print(int a) {cout << "Integer: " << a << endl;
}void print(double a) {cout << "Double: " << a << endl;
}int main() {print(5);       // 调用 print(int)print(3.14);    // 调用 print(double)return 0;
}

在这个例子中,print(5) 完全匹配 print(int a)print(3.14) 完全匹配 print(double a)

2. 类型转换的匹配

如果没有找到完全匹配的重载版本,编译器会尝试进行类型转换,以便找到最合适的函数。例如:

void print(int a) {cout << "Integer: " << a << endl;
}void print(double a) {cout << "Double: " << a << endl;
}int main() {print(5.5);  // 5.5 是 double 类型,编译器会将它自动转换成 int 类型调用 print(int)return 0;
}

编译器会将 5.5 转换为 int 类型并调用 print(int a)

3. 最小化转换

编译器会选择最少转换的重载版本。如果一个重载版本需要更多的类型转换,它的优先级较低。例如:

void print(int a) {cout << "Integer: " << a << endl;
}void print(double a) {cout << "Double: " << a << endl;
}void print(float a) {cout << "Float: " << a << endl;
}int main() {print(5);       // 调用 print(int)print(5.0);     // 调用 print(double)print(5.0f);    // 调用 print(float)return 0;
}

对于 print(5),编译器会优先选择 print(int a),而不会选择 print(double a)print(float a),因为它不需要进行类型转换。

4. 函数重载的最优匹配

如果两个重载版本都符合条件,编译器会选择最符合的版本。例如:

void print(int a) {cout << "Integer: " << a << endl;
}void print(char* a) {cout << "String: " << a << endl;
}void print(void* a) {cout << "Pointer: " << a << endl;
}int main() {print(5);         // 调用 print(int)print("hello");   // 调用 print(char*)print(NULL);      // 调用 print(void*)return 0;
}

这就是函数重载中最优匹配的规则:print(5) 显式调用了 print(int)print("hello") 调用了 print(char*),而 print(NULL) 则选择了 print(void*),因为 NULL 是一个指针常量。

重载函数常见问题

虽然函数重载非常有用,但在实际开发中,仍然存在一些潜在问题,我们需要注意。

1. 重载歧义

如果编译器无法明确选择最合适的重载版本,程序就会发生歧义,导致编译错误。例如:

void print(int a) { cout << "Integer: " << a << endl; }
void print(double a) { cout << "Double: " << a << endl; }int main() {print(5);    // 编译错误,编译器无法确定调用 print(int) 还是 print(double)return 0;
}

原因:如果我们传递 5,它既可以被转换成 int 类型,也可以转换成 double 类型,编译器无法决定调用哪个版本的函数,从而导致歧义。

解决方法:避免传递可以隐式转换为多种类型的参数,或者明确指明调用的函数版本:

2. 默认参数与重载

在某些情况下,默认参数可能与函数重载发生冲突。比如:

void print(int a = 0) { cout << "Integer: " << a << endl; }
void print(double a) { cout << "Double: " << a << endl; }int main() {print();     // 编译错误:默认参数与重载函数冲突return 0;
}

原因print() 会被解释为 print(int a),但默认值 a = 0 又使得 print()print(int) 发生重载冲突。

3. 函数重载与运算符重载的冲突

运算符重载与函数重载可能会产生一些混淆。特别是在运算符重载函数的参数类型与普通函数重载函数的参数类型非常接近时,会出现歧义。

函数重载的优化技巧

1. 避免不必要的重载

在一些情况下,函数重载会导致代码冗余,增加维护的难度。例如,如果两个重载函数之间只有一个小的差别,考虑将它们合并为一个函数,利用 可变参数模板 来实现。

void print(int a) { cout << "Integer: " << a << endl; }
void print(double a) { cout << "Double: " << a << endl; }// 使用模板重载函数
template<typename T>
void print(T a) {cout << "Value: " << a << endl;
}int main() {print(10);      // 调用 print<int>print(3.14);    // 调用 print<double>return 0;
}
2. 使用 std::variantstd::any 代替过多的重载

如果你的函数重载仅仅是为了支持多种数据类型的处理,可以考虑使用 std::variantstd::any 来避免过多的重载。

#include <iostream>
#include <variant>void print(std::variant<int, double, std::string> value) {std::visit([](auto&& arg) { std::cout << arg << std::endl; }, value);
}int main() {print(10);           // 输出: 10print(3.14);         // 输出: 3.14print("Hello");      // 输出: Helloreturn 0;
}

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

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

相关文章

14-H指数

给你一个整数数组 citations &#xff0c;其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数。 根据维基百科上 h 指数的定义&#xff1a;h 代表“高引用次数” &#xff0c;一名科研人员的 h 指数 是指他&#xff08;她&#xff09;至少发…

关于es6-module的语法

ES6&#xff08;ECMAScript 2015&#xff09;引入了模块化的概念&#xff0c;旨在使 JavaScript 更加模块化、可维护和可重用。ES6 模块允许我们在不同的文件中组织和管理代码&#xff0c;使得不同模块之间的依赖关系更加清晰。 1. 导出&#xff08;Export&#xff09; 1.1 命…

Chrome多开终极形态解锁!「窗口管理工具+IP隔离插件

Web3项目多开&#xff0c;继ads指纹浏览器钱包被盗后&#xff0c;更多人采用原生chrome浏览器&#xff0c;当然对于新手&#xff0c;指纹浏览器每月成本也是一笔不小开支&#xff0c;今天逛Github发现了这样一个解决方案&#xff0c;作者开发了窗口管理工具IP隔离插件&#xff…

DeepSeek核心算法解析:如何打造比肩ChatGPT的国产大模型

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》&#xff08;人工智能科学与技术丛书&#xff09;【陈敬雷编著】【清华大学出版社】 文章目录 DeepSeek大模型技术系列一DeepSeek核心算法解析&#xff1a;如何…

arm 入坑笔记

1.开发环境&#xff08;IDE&#xff09;使用keil_5 (keil_mdk) 2.两个手册需要关注&#xff1a;用户手册&#xff08;编程需要&#xff09;&#xff0c;数据手册&#xff08;硬件&#xff09; 3.32bit地址空间&#xff1a;0~2^324GB寻址空间及&#xff08;0-FFFF_FFFF&#x…

弱监督语义分割学习计划(0)-计划制定

经过与deepseek的一番讨论和交流&#xff0c;DeepSeek为我设计了一个30天高强度学习计划&#xff0c;重点聚焦弱监督/无监督语义分割在野外场景的应用&#xff0c;结合理论与实践&#xff0c;并最终导向可落地的开源项目。以下是详细计划&#xff1a; 总体策略 优先级排序&…

vscode远程报错:Remote host key has changed,...

重装了Ubuntu系统之后&#xff0c;由20.04改为22.04&#xff0c;再用vscode远程&#xff0c;就出现了以上报错。 亲测有效的办法 gedit ~/.ssh/known_hosts 打开这个配置文件 删掉与之匹配的那一行&#xff0c;不知道删哪一行的话&#xff0c;就打开第一行这个 /.ssh/confi…

Python - 爬虫利器 - BeautifulSoup4常用 API

文章目录 前言BeautifulSoup4 简介主要特点&#xff1a;安装方式: 常用 API1. 创建 BeautifulSoup 对象2. 查找标签find(): 返回匹配的第一个元素find_all(): 返回所有匹配的元素列表select_one() & select(): CSS 选择器 3. 访问标签内容text 属性: 获取标签内纯文本get_t…

DeepSeek驱动下的数据仓库范式转移:技术解耦、认知重构与治理演进

DeepSeek驱动下的数据仓库范式转移&#xff1a;技术解耦、认知重构与治理演进 ——基于多场景实证的架构革命研究 一、技术解耦&#xff1a;自动化编程范式的演进 1.1 语义驱动的ETL生成机制 在金融风控场景中&#xff0c;DeepSeek通过动态语法树解析&#xff08;Dynamic Syn…

代码随想录算法训练营day38(补0206)

如果求组合数就是外层for循环遍历物品&#xff0c;内层for遍历背包。 如果求排列数就是外层for遍历背包&#xff0c;内层for循环遍历物品。 1.零钱兑换 题目 322. 零钱兑换 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c…

golang channel底层实现?

底层数据实现 type hchan struct { qcount uint // 当前队列中的元素数量 dataqsiz uint // 环形队列的大小 buf unsafe.Pointer // 指向环形队列的指针 elemsize uint16 // 元素大小 closed uint32 // chan…

图的最小生成树算法: Prim算法和Kruskal算法(C++)

上一节我们学习了最短路径算法, 这一节来学习最小生成树. 最小生成树(Minimum Spanning Tree, MST)算法是图论中的一种重要算法, 主要用于在加权无向图中找到一棵生成树, 使得这棵树包含图中的所有顶点, 并且所有边的权重之和最小. 这样的树被称为最小生成树. 最小生成树广泛应…

矩阵系统源码搭建的数据管理开发功能解析,支持OEM

一、引言 在矩阵系统中&#xff0c;数据犹如血液&#xff0c;贯穿整个系统的运行。高效的数据管理开发功能是确保矩阵系统稳定、可靠运行的关键&#xff0c;它涵盖了数据的存储、处理、安全等多个方面。本文将深入探讨矩阵系统源码搭建过程中数据管理功能的开发要点。 二、数据…

DeepSeek 助力 Vue 开发:打造丝滑的日期选择器(Date Picker),未使用第三方插件

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

操作系统知识点2

1.P&#xff0c;V操作可以实现进程同步&#xff0c;进程互斥&#xff0c;进程的前驱关系 2.先来先服务调度算法是不可抢占的算法 3.UNIX操作系统中&#xff0c;对文件系统中空闲区的管理通常采用成组链接法 4.对于FAT32文件系统&#xff0c;它采用的是链接结构 5.不同的I/O…

【个人开发】deepspeed+Llama-factory 本地数据多卡Lora微调【完整教程】

文章目录 1.背景2.微调方式2.1 关键环境版本信息2.2 步骤2.2.1 下载llama-factory2.2.2 准备数据集2.2.3 微调模式2.2.3.1 zero-1微调2.2.3.2 zero-2微调2.2.3.3 zero-3微调2.2.3.4 单卡Lora微调 2.2.4 实验2.2.4.1 实验1&#xff1a;多GPU微调-zero12.2.4.2 实验2&#xff1a;…

iOS 中使用 FFmpeg 进行音视频处理

在 iOS 中使用 FFmpeg 进行音视频处理,通常需要将 FFmpeg 的功能集成到项目中。由于 FFmpeg 是一个 C 库,直接在 iOS 中使用需要进行一些配置和封装。 1. 在 iOS 项目中集成 FFmpeg 方法 1:使用 FFmpeg 预编译库 下载 FFmpeg iOS 预编译库: 可以从以下项目中获取预编译的 …

Elasticsearch:将 Ollama 与推理 API 结合使用

作者&#xff1a;来自 Elastic Jeffrey Rengifo Ollama API 与 OpenAI API 兼容&#xff0c;因此将 Ollama 与 Elasticsearch 集成非常容易。 在本文中&#xff0c;我们将学习如何使用 Ollama 将本地模型连接到 Elasticsearch 推理模型&#xff0c;然后使用 Playground 向文档提…

openGauss 3.0 数据库在线实训课程18:学习视图管理

前提 我正在参加21天养成好习惯| 第二届openGauss每日一练活动 课程详见&#xff1a;openGauss 3.0.0数据库在线实训课程 学习目标 掌握openGauss视图的管理&#xff1a;创建视图、删除视图、查询视图的信息、修改视图的信息。 课程作业 1.创建表&#xff0c;创建普通视图…

腾讯云大模型知识引擎×DeepSeek赋能文旅

腾讯云大模型知识引擎DeepSeek赋能文旅 ——以合肥文旅为例的技术革新与实践路径 一、技术底座&#xff1a;知识引擎与DeepSeek的融合逻辑 腾讯云大模型知识引擎与DeepSeek模型的结合&#xff0c;本质上是**“知识库检索增强生成&#xff08;RAG&#xff09;实时联网能力”**…