03【C++ 入门基础】函数重载

文章目录

  • 引言
  • 函数重载
    • 函数重载的使用
    • 函数重载的原理
      • extern “C”
    • 静态多态
  • 总结


引言

通过00【C++ 入门基础】前言得知,C++是为了解决C语言在面对大型项目的局限而诞生:

C语言面对的现实工程问题(复杂性、可维护性、可扩展性、安全性)

C语言为了实现不同类型的加法,只能定义多个函数:

//C语言:
int add_int(int a, int b)              //对两int类型相加
{return a + b;
}
double add_double(double a, double b)  //对两double类型相加
{return a + b;
}
int add_10_int_ptr(void *a)      //对void*类型强转之后加10
{return *a + 10;
}
int main()
{add_int(1, 2);add_double(1.2, 2.3);return 0;
}

平时写写还可以,面对大型项目问题就暴露出来了:

  1. 多个类似功能(加法)的函数,在各个角落被调用,但是函数名称不同,命名冗余,代码可读性下降。​(对调用者来说复杂)
  2. C语言的泛型,由于对void*没有检查,使用起来不安全(安全性):
#include <stdio.h>
// 类型不安全的打印函数
void unsafe_print(void* data, char type)   //C语言使用void*,也实现了一个函数接收不同类型(泛型编程),但是使用起来不安全。
{switch(type) {case 'i': // 假设 'i' 代表 intprintf("Integer: %d\n", *(int*)data);break;case 'f': // 假设 'f' 代表 floatprintf("Float: %f\n", *(float*)data);break;default:printf("Unknown type\n");}
}
int main() 
{int a = 10;float b = 3.14;// 正确使用(需要确保类型匹配)unsafe_print(&a, 'i');unsafe_print(&b, 'f');// 类型错误的使用(编译器不会警告)unsafe_print(&b, 'i'); // 错误!把 float 当作 int 解释unsafe_print(&a, 'f'); // 错误!把 int 当作 float 解释return 0;
}

函数重载

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

函数重载的使用

#include<iostream>
using namespace std;
// 1、参数类型不同
int Add(int left, int right)
{cout << "int Add(int left, int right)" << endl;return left + right;
}
double Add(double left, double right)
{cout << "double Add(double left, double right)" << endl;return left + right;
}
// 2、参数个数不同
void f()
{cout << "f()" << endl;
}
void f(int a)
{cout << "f(int a)" << endl;
}
// 3、参数类型顺序不同
void f(int a, char b)
{cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{cout << "f(char b, int a)" << endl;
}
int main()
{Add(10, 20);Add(10.1, 20.2);f();f(10);f(10, 'a');f('a', 10);return 0;
}

函数重载,需要参数类型不同、参数个数不同、参数类型顺序不同。

思考:

  1. 以下函数构成重载吗?
int Add(int a, char b)
{cout << "Add(int a, char b)";
}
int Add(int b, char a)
{cout << "Add(int b, char a)";
}

不构成,我们的参数的顺序类型个数都没有变化,参数名不起决定作用。

  1. 以下函数构成重载吗?
int f()
{cout << "f()";
}
int f(int a = 0)
{cout << "f(int a = 0)";
}

构成,但是有错,因为调用的时候有歧义:

int main()
{f();      //有多个 重载函数 "f" 实例与参数列表匹配:return 0;
}
  1. 以下函数构成重载吗?
void Func(int a = 10)
{cout<<"void Func(int a = 10)"<<endl;
}
void Func(int a)
{cout<<"void Func(int a)"<<endl;
}

不构成,不满足重载条件,虽然不传参只能调用到第一个带缺省值的函数,但是如果传参的话就会有调用冲突:“Func”: 重定义默认参数 : 参数 1

函数重载的原理

在从代码变为可执行程序的过程的汇编阶段,会生成符号表,符号表主要功能之一就是帮助后续的链接器找到和解析符号(如函数和全局变量)。
编译链接过程
那么每个函数都会生成它自己对应的符号,C++在这里用了采用了有别于C语言的符号生成方式,才得以实现函数重载:

  • 采用C语言编译器编译后结果:
    C语言编译后的结果
  • 采用C++编译器编译后结果:
    在这里插入图片描述

可以看到,C++最终生成的符号表,是不同于C语言的,其实它生成符号表的时候,考虑了函数的参数,也就最终导致,参数类型不同、参数个数不同、参数类型顺序不同的同名函数生成的符号不相同,那么最后链接器去找到对应函数的符号进行链接的时候,就可以区分重载的函数!!!

疑问:我们只是换了个编译器,为什么说是C++语言的行为?
这由C++语言规范明确规定的机制,强制所有的C++编译器执行。

windows下vs编译器对函数名字修饰规则相对复杂难懂,所以我们使用Linux演示,但道理都是类似的,我们就不做细致的研究了。

extern “C”

由于C和C++编译器对函数名字修饰规则的不同,在有些场景下可能就会出问题,比如:

  1. C++中调用C语言实现的静态库或者动态库,反之亦然。
  2. 多人协同开发时,有些人擅长用C语言,有些人擅长用C++。

在这种混合模式下开发,由于C和C++编译器对函数名字修饰规则不同,可能就会导致链接失败,在该种场景下,就需要使用extern “C”。在函数前加extern “C”,意思是告诉编译器,将该函数按照C语言规则来编译。下面演示一个在C++工程中使用C语言静态库的例子:

///cal.h/
#pragma once
/*
* 注意:
* 在实现该库时,并不知道将来使用该静态库的工程是C语言工程还是C++工程
* 为了能在C/C++工程中都能使用,函数声明时需加上extern "C"
*
* __cplusplus:是C++编译器中定义的宏,即用该宏来检测是C工程还是C++工程
*
* 
* #ifdef __cplusplusextern "C"{#endif// 要导出函数的声明#ifdef __cplusplus}#endif作用:如果是C++工程,编译器已经定义_cplusplus宏,编译时该宏是可以被识别的,被声明的函数就被
extern "C"修饰了,此时C++编译就知道,静态库中的函数是按照C的方式编译的,这样在链接时就会按照C的方式找函
数名字如果是C工程,编译器未定义_cplusplus宏,编译时该宏无法被是被,则条件编译就无效,函数就
不会被extern "C"修饰
*/#ifdef __cplusplus
extern "C"
{
#endifint Add(int left, int right);int Sub(int left, int right);
#ifdef __cplusplus
}
#endif
///cal.cpp/
#include "cal.h"int Add(int left, int right)
{return left + right;
}
int Sub(int left, int right)
{return left - right;
}
///libUser.cpp/
#include "./../../StaticLib/StaticLib/cal.h"
#pragma comment(lib, "./../../StaticLib/Debug/StaticLib.lib")
#include <iostream>
using namespace std;int main()
{int ret = Add(10, 20);cout << ret << endl;ret = Sub(30, 20);cout << ret << endl;return 0;
}

如果在实现静态库时,cal.h中的函数没有使用extern "C"修饰就会报错:

#pragma once
int Add(int left, int right);
int Sub(int left, int right);

1>TestCalc.cpp
1>TestCalc.obj : error LNK2019: 无法解析的外部符号 “int __cdecl Add(int,int)” (?
Add@@YAHHH@Z),函数 _main 中引用了该符号
1>TestCalc.obj : error LNK2019: 无法解析的外部符号 “int __cdecl Sub(int,int)” (?
Sub@@YAHHH@Z),函数 _main 中引用了该符号
1>D:\WorkStations\Gitee\cppLesson\TestCalc\Debug\TestCalc.exe : fatal error LNK1120: 2
个无法解析的外部命令

思考:

  1. C语言中为什么不能支持函数重载?
    C语言没有生成符号表是考虑参数的规范,所以链接器最后会将所有同名不同参的函数,认为是同一个。
  2. C++中能否将一个函数按照C的风格来编译?
    可以,为了向前兼容,即即使有了C++,C语言的代码也可以被C++的编译器运行。
  3. C++程序中被extern "C"修饰的函数,就会按照C语言规则来编译,用C语言的方式来生成符号表,那么它是不是不可以做函数重载了?
    是的,C语言规则生成符号表时不考虑函数的参数,所以被修饰的函数将不可被重载。

静态多态

多态,就是同一个接口(函数调用)可以根据对象类型的不同而执行不同的操作,说白了就是看人下菜碟。
多态又分为静态多态和动态动态,静态多态在编译时绑定,动态多态在运行时绑定。

我们的函数重载,就正是多态的一种,因为它根据参数类型变化,可以执行不同操作,它在我们编译时,便通过符号表确定,所以它是一种静态多态!!

总结

1.函数重载,解决C语言的同功能但不同命函数的命名冗余和一些安全性问题。
2.函数重载的使用方式,是定义一个同名,但是参数类型不同、参数个数不同、参数类型顺序不同的函数。
3.函数重载的原理是:C++汇编时生成的符号表,每个函数的符号都考虑了函数的参数去生成,所以参数类型不同、参数个数不同、参数类型顺序不同的同名函数之间可以被区分。
4.函数重载,是一种多态,静态多态。


本文章为作者的笔记和心得记录,顺便进行知识分享,有任何错误请评论指点:)。

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

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

相关文章

改写一个小项目: flask -------------------> next js

现在&#xff0c;请把上面改写代码的过程中&#xff0c;所有的过程&#xff0c;都写下来&#xff0c;写为文章的形式&#xff0c;内容比较长&#xff0c;你可以分多次输出。而且要求语言幽默&#xff0c;苦中作乐的心态。分条理&#xff0c;要清晰。一场从 Flask 到 Next.js 的…

线性相关和线性无关

在线性代数中&#xff0c;线性相关和线性无关是刻画向量组性质的核心概念&#xff0c;以下是关于它们的重要结论总结&#xff1a; 一、基本定义与核心判定 线性相关的定义 向量组 { α 1 , α 2 , … , α m } \{\alpha_1, \alpha_2, \dots, \alpha_m\} {α1​,α2​,…,αm​…

非常有科技感的wpf GroupBox 控件

效果 样式 <Style TargetType="GroupBox"><Setter Property="Margin" Value="10,5" /><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="GroupBox"><Grid>&l…

【Java--SQL】${}与#{}区别和危害

目录 一区别 二SQL${}注入问题 一区别 在MyBatis中&#xff0c;#{}和${}是两种不同的参数占位符&#xff0c;用于在SQL语句中引用变量或参数。它们的区别如下&#xff1a; 1.#{}占位符&#xff08;预编译&#xff09;&#xff1a;#{}是MyBatis中的预编译占位符&#xff0c;…

【科技核心期刊推荐】计算机与数字工程

【论文发表利器】《计算机技术与工程应用》——工程技术领域的学术新天地&#xff01; 作为计算机技术与工程应用交叉领域的前沿期刊&#xff0c;《计算机技术与工程应用》期刊&#xff0c;聚焦算法、系统结构、信息融合与安全、图像处理等方向&#xff0c;为学术界提供了一个…

导出docker-compse.yml中docker镜像成tar文件

#!/bin/bash # 确保脚本在正确的目录下运行 SCRIPT_DIR$(dirname "$(realpath "$0")") cd "$SCRIPT_DIR" || exit 1 # 定义docker-compose文件路径 COMPOSE_FILE"${SCRIPT_DIR}/docker-compose.yml" # 创建导出目录 EXPORT_DIR"$…

ECMAScript 2019(ES2019):数组与对象操作的精细化升级

1.版本背景与发布 发布时间&#xff1a;2019年6月&#xff0c;由ECMA International正式发布&#xff0c;标准编号为ECMA-262 10th Edition。历史意义&#xff1a;作为ES6之后的第四次年度更新&#xff0c;ES2019聚焦于数组、对象和字符串操作的精细化改进&#xff0c;提升开发…

2.1.1 配置堡垒机以控制Linux资产文件传输

文章目录 一、试题及考试说明二、操作步骤1. 启动JumpServer服务&#xff0c;浏览器登录&#xff08;admin/Sjtu1896&#xff09;2. 创建堡垒机用户&#xff0c;用户名为“ops01”格式命名&#xff0c;邮箱以为“ops01jumpserver.cn”&#xff0c;使用密码“admin123”&#xf…

react ant-design通用页面自适应适配不同分辨率屏幕的方法工具类

该方法会根据 目标分辨率&#xff08;options.width/height&#xff09; 和 当前窗口尺寸&#xff08;innerWidth/innerHeight&#xff09; 计算缩放比例&#xff0c;并保持 等比例缩放&#xff08;Math.min(scaleX, scaleY)&#xff09;&#xff0c;确保内容不变形&#xff1a…

基于IEC61499实现的工业机器视觉方案

1.什么是机器视觉 机器视觉就是赋予机器看懂图像的能力。它是一门涉及人工智能、计算机科学、图像处理、光学、机械工程和自动化的交叉技术领域。核心目标是&#xff1a;通过摄像头或其他成像设备获取图像或视频&#xff0c;然后利用计算机算法对这些图像进行分析和理解&#x…

机电一体化论文写作实战指南:从创新设计到工程验证的完整路径

机电论文的“技术-表达”鸿沟 某高校团队研发的智能抓取系统实物表现优异&#xff0c;却被审稿人质疑&#xff1a; “未说明机电耦合设计对性能的影响”——这揭示了机电一体化研究的核心痛点&#xff1a;强工程弱理论。本文基于217篇高影响力论文&#xff0c;拆解从技术到写作…

MySQL 配置参数调优:根据工作负载调整服务器设置

MySQL 数据库的默认配置参数是为了适应各种通用场景而设定的,它们通常无法最大化发挥服务器硬件的潜力,也无法完全匹配特定应用程序的工作负载。一个未优化的 MySQL 配置,在面对高并发、大数据量或特定查询模式时,很容易成为系统性能的瓶颈。 配置参数调优,就是根据你的服…

嵌入式Linux驱动开发基础-2 LED驱动

imx6ull中GPIO涉及寄存器 1&#xff1a;CCM寄存器 GPIOx 要用 CCM_CCGRy 寄存器中的 2 位来决定该组 GPIO 是否使能。哪组 GPIO 用哪个 CCM_CCGR 寄存器来设置。 CCM_CCGR 寄存器中某 2 位的取值含义如下&#xff1a; 00 &#xff1a;该 GPIO 模块全程被关闭 01 &…

深度解析】使用Go语言实现JWT:从原理到实践

JWT&#xff08;JSON Web Token&#xff09;已成为现代Web应用中身份验证的基石。本文深入剖析如何用Go语言实现JWT&#xff0c;从基础概念、底层机制到完整代码实践&#xff0c;助你全面掌握。 一、JWT概述 JWT是一种开放标准&#xff08;RFC 7519&#xff09;&#xff0c;用…

深入解读 DeepSeek-V3 架构及落地的挑战

从多专家架构&#xff08;MoE&#xff09;到模型落地实战的一线观察 一、引言&#xff1a;DeepSeek-V3 是什么&#xff1f; 在大模型百花齐放的今天&#xff0c;DeepSeek-V3 作为 DeepSeek 系列的第三代开源模型&#xff0c;不仅延续了高质量对话能力&#xff0c;还在架构上迈…

前端进阶之路-从传统前端到VUE-JS(第二期-VUE-JS框架结构分析)

经过上期内容的学习&#xff0c;我们已经可以构建一个VUE-CLI框架了&#xff0c;接下来我们分析一下这个框架&#xff0c;毕竟知己知彼&#xff0c;百战百胜 我们创建完成后可以看到以下内容 接下来我们分析一下他的文件结构 node_modules用于存放项目所依赖的第三方模块和包…

网络协议 / 加密 / 签名总结

加密方式&#xff1a; 对称加密&#xff1a;key 不可公开。 非对称加密&#xff1a;公钥加密的信息只有私钥能解密。私钥加密的信息只有公钥能解密&#xff0c;且公钥只能解密私钥加密的信息&#xff08;用于签名&#xff09;。 非对称加密应用&#xff1a; 签名&#xff1a…

集成学习基础:Bagging 原理与应用

本文由「大千AI助手」原创发布&#xff0c;专注用真话讲AI&#xff0c;回归技术本质。拒绝神话或妖魔化。搜索「大千AI助手」关注我&#xff0c;一起撕掉过度包装&#xff0c;学习真实的AI技术&#xff01; Bagging 介绍 1. 定义与全称&#xff1a; Bagging 是 Bootstrap Agg…

skiaSharp linux 上报错

The type initializer for SkiaSharp.SKImageInfo threw an exception 这个错误表明在 Linux 系统上初始化 SkiaSharp 的 SKImageInfo 类型时出现了问题。以下是完整的解决方案&#xff1a; 安装系统依赖&#xff1a; # Ubuntu/Debian sudo apt-get update sudo apt-get ins…

crawl4ai crawler.arun( 超时问题

delay_before_return_html500 # 单位&#xff1a;毫秒 会导致 crawler.arun 超时问题。按理说不应该 await crawler.arun( 1. 浏览器加载页面 ✅ 2. 页面DOM构建完成 ✅ 3. JavaScript执行完成 ✅ 4. 等待 delay_before_return_html 时间 ⏳ (500ms) 5. 返回最终HTML内容 &…