我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C++ 函数重载背后的原理

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C++ 函数重载背后的原理

一、函数重载

我们可能对函数很是熟悉,但是重载又是什么意思呢?我们先来用一个具体的场景来分享.

一天,张三的老板要你写一个两位数相加的函数,张三心想这不很简单吗?手指一动,结果就出来了,挺简单的嘛.

int add(int x, int y)
{
	return x + y;
}

现在老板看张三的代码立马火了,你是怎么想的,要是我想12,10.9相加呢?你这个就只能两个整型相加,回去修改!!!张三听到老板的话不由得反驳道:这怎么改,总不能再写一个add1,add2…吧.老板听到了张三的嘟囔,生气道,你没有学过函数重载吗?看看下面的代码,回去好好学习学习,基础都不扎实.

张三看到代码,不由大吃一惊,C++还可以这么写?好神奇啊,我要好好看看书.

int add(int x, int y)
{
	return x + y;
}
double add(double x, int y)
{
	return x + y;
}
double add(int x, double y)
{
	return x + y;
}

我们可不希望张三这种事发生在我们身上,先来看看函数重载的定义

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

可能大家不喜欢看定义,我这里给一个总结.

函数重载要满足下面的要求.

  • 函数名相同
  • 参数的类型,个数,顺序有一个不同就可以了
  • 返回类型不做要求

二、函数重载的原理

一般情况下,我们知道了函数重载到会应用就可以了,但是对于我们来说需要我们看看他们的原理,为什么C语言不支持重载,C++支持重载?这些都是问题.

三、为何C++可以支持重载

我们先用C++的编译器简单的看看如何执行程序,下面是我在Linux环境下使用g++来完成的,大家要是不太懂,可以先不管,直接理解C++的原理.

我们先来看看现象,发现C++可以精准的找到需要匹配的函数,这是我们所疑惑的.

// test.h
#pragma once 
#include <iostream>
#include <stdio.h>
using std::cout;
void func(int a, double b);
void func(double a, int b);
//test.cpp
#include "test.h"
//写两个函数   函数形成重载
void func(int a, double b)
{
	printf("%d %lf", a, b);
}
void func(double a, int b)
{
	printf("%lf %d", a, b);
}
//Mian.cpp

#include "test.h"
int main()
{
	func(10, 2.20);
	return 0;
}

1.程序的编译链接

关于这一点,我们先简单的说说,之前我们详细的谈过.一个文件变成一个可执行程序需要经过下面4个步骤.

  • 预处理 宏替换 头文件展开 注释替换 main.cpp -> main.i test.cpp -> test.i
  • 编译 检查语法 ,代码变换成汇编语言 main.i -> main.s test.i -> test.s
  • 汇编 汇编语言变成二进制语言,各个文件变成目标文件 main.s -> main.o test.s -> test.o
  • 链接 多个目标文件+链接库发生链接

这里我们需要重点谈谈链接,这是我们今天最重要的一部分

链接就仅仅只是目标文件的合并吗?不是的,它要完成的任务很多,其中最重要的就是<font color = red>找到函数的地址,链接对应上,合并到一起</font>

当我们进行过头文件的展开后,Main.cpp中有func函数的声明和调用.在编译和汇编的过程中存在一个符号表,这个符号表记录了函数的定义以及相应的映射.这是很重要的.符号表里面包含了函数名和函数的地址.

每一个目标文件(.o)都包含一个符号表和一系列指令,我们看看入和完成函数链接.

现在到mian.o的指令这里了,前面的一些列指令都正常经行,直到它遇到了func这个点,要是看过C语言的汇编语言的朋友们可能对下面的比较熟悉.

到了func这里,编译器开始call (func: ?),编译器不知道func的地址,但是前面头文件的的展开中func函数已经声明了,所以编译器知道了func是一个函数.就先给它一个无效的地址.当程序进行链接时,编译器一看它是一个无效地址,会拿函数名和其他的.o文件里面的符号表去碰,碰到了就填上,找不到就会报连接错误.

四、C语言为何不支持重载

到这里就可以明白了,当我们拿函数名去碰的时候,符号表里面存在多个相同的函数名,编译器就不会识别该用哪个.更何况存在相同函数名的.c文件有时都不可能编译过.

gcc对函数名都不会做任何处理,这也是C语言不支持函数重载的原因.

1.C++为何可以支持函数重载

到这里我们就可以得到了结果,既然在链接的时候无效的函数会拿函数名去其他的符号表里面去碰,那么只要我们看看重载的函数名像不像同就可以了,大家可能会有些疑惑,重载的函数名不是相同的吗?是的,但是C++编译器会做一定的处理.这里每个编译器都有自己的函数名修饰规则 这就是C++ 支持重载的原理.

这就是C可以支持重载的原因,g的函数修饰后变成【_Z+函数名长度+函数名+类型首字母1+类型首字母2…】,也是我们只对参数列表做了要求,对返回值不做要求的原因.

五、C++和C语言相互调用

我们都知道C++支持C语言的大部分语法,C++和C语言可以相互调用吗?实际上是可以的,在一个大型程序中,有的部门可能使用的是C写的的函数,有的部门可能用的C++,要是他们不能相互使用那就打脸了.

1.创建静态库

我们可以把自己写的代码编译成一个静态库或者动态库,这里我以静态库举例,看看如何在VS中中创建一个静态库.

2.C++调用C

我们已经有了一个C语言的静态库,现在有一个C++的项目需要使用这个静态库,我们该如何使用呢?需要分为下面几个步骤

下面这两张图片都是修改环境的设置,我使用的是VS2013,其他的大概应该差不多,大家依次来修改就可以了.

到这里我们就可以调用C语言的静态库了,让我们来看看结果吧.

#include "../../Heap/Heap/heap.h"  //相对路径
int main()
{
	MyHeap myHeap;
	InitMyHeap(&myHeap);
	HeapPush(&myHeap, 1);
	HeapPush(&myHeap, 2);
	HeapPush(&myHeap, 3);
	Display(&myHeap);
	return 0;
}

这为什么报错?我们不是已经设置好了静态库了吗?实际上这种错误是很容易分析出来的,当C++去调用C语言的函数时,C++会自动修改函数名,当时C语言不会啊,所以他们就不会碰到一起,链接就会出错.

extern “C”

既然编译器不能自动识别C语言的函数名,我们告诉编译器一下不就可以了吗.extern “C” 就是这种作用.

有时候在C++工程中可能需要将某些函数按照 C 的风格来编译在函数前加 extern “C” ,意思是告诉编译器,
将该函数按照
 C 语言规则来编译。比如:tcmalloc是google用C++实现的一个项目,他提供tcmallc()和tcfree
两个接口来使用,但如果是C项目就没办法使用,那么他就使用extern “C”来解决

extern "C"   // 告知这是C语言的函数声明
{
	#include "../../Heap/Heap/heap.h"
}
int main()
{
	MyHeap myHeap;
	InitMyHeap(&myHeap);
	HeapPush(&myHeap, 1);
	HeapPush(&myHeap, 2);
	HeapPush(&myHeap, 3);
	Display(&myHeap);
	return 0;
}

extern “C” 原理

我们需要来看看extern “C” 的原理,使用了extern “C” 后,在C++在进行编译的时候函数名字就依据C语言的方法来修改了,不在变成C++ 的规则.extern "C"可以单独修饰函数,也可以修饰一系列函数,使用代码块.

// test.h
#pragma once 
#include <iostream>
#include <stdio.h>
extern "C" void func(int a, double b);
//test.cpp

#include "test.h"
//写两个函数   函数形成重载
void func(int a, double b)
{
	printf("%d %lf", a, b);
}
//Mian.cpp

#include "test.h"
int main()
{
	func(10, 2.20);
	return 0;
}

3.C语言调用C++

那么C语言可以调用C++ 的吗?可以了,不过也需要一些段来完成.如何让C语言去识别C++的规则呢?这是我们需要考虑的.

我们已经把库改成的了C++的静态库了.

#include "../../Heap/Heap/heap.h"
int main()
{
	MyHeap myHeap;
	InitMyHeap(&myHeap);
	HeapPush(&myHeap, 1);
	HeapPush(&myHeap, 2);
	HeapPush(&myHeap, 3);
	Display(&myHeap);
	return 0;
}

我们无法让C语言的编译器去识别C++ 的函数的命名,那么我们是不是可以在函数一编译的时候就完成函数名依照C语言来说.这就很简单了.

但是即使是这样,C语言仍旧会报错,原因在于在头文件展开的时候,C语言根本不识别extern “C”,所以我们就需要条件编译了.

使用条件编译来修改的静态库的方法如下,需要再次编译.

//方法一
#ifdef __cplusplus    // C++独有的
	#define EXTERNC extern "C"
#else 
	#define EXTERNC
#endif


EXTERNC extern void InitMyHeap(MyHeap * pHeap);

EXTERNC extern void HeapPush(MyHeap* pHeap, HPDataType x);
EXTERNC extern bool IsFull(MyHeap* pHeap);
EXTERNC extern bool IsEmpty(MyHeap* pHeap);
EXTERNC extern int HeapSize(MyHeap* pHeap);
EXTERNC extern void adjustDown(MyHeap* pHeap);
EXTERNC extern void adjustUp(MyHeap* pHeap);
EXTERNC extern void Display(MyHeap* pHeap);
EXTERNC extern HPDataType HeapTop(MyHeap* pHeap);
EXTERNC extern void HeapPop(MyHeap* pHeap);
//方法 二
#ifdef __cplusplus
extern "C"
{
#endif

	extern void InitMyHeap(MyHeap * pHeap);

	extern void HeapPush(MyHeap* pHeap, HPDataType x);
	extern bool IsFull(MyHeap* pHeap);
	extern bool IsEmpty(MyHeap* pHeap);
	extern int HeapSize(MyHeap* pHeap);
	extern void adjustDown(MyHeap* pHeap);
	extern void adjustUp(MyHeap* pHeap);
	extern void Display(MyHeap* pHeap);
	extern HPDataType HeapTop(MyHeap* pHeap);
	extern void HeapPop(MyHeap* pHeap);
#ifdef __cplusplus
}
#endif

这样就解决了.

注意,这里有一点需要注意的,当我们C语言调用C++静态库的时候,最起码我们实际需要的的那部分代码在extern "C"修饰的函数中不能发生重载.

六、C++ 注意事项

这个注意事项主要是依据extern "C"来谈的,有些比较偏僻的内容需要关注下.

1.extern "C"修饰的函数和一个函数完全一样

在extern "C"修饰的函数模块外面存在了一个完全一摸一样的的函数,这个编译器不会给通过的.

#ifdef __cplusplus
extern "C"
{
#endif 
	void func(int a, int b)
	{
		printf("C : %d %d\n", a, b);
	}
#ifdef __cplusplus
}
#endif 
//完全一样
void func(int a, int b)
{
	printf("C : %d %d\n", a, b);
}

2.extern "C"修饰的函数和一个函数构成重载

在extern "C"修饰的函数模块外面一个函数构成重载这种编译器可以通过的,但是extern "C"修饰的命名方法仍旧还是按照C语言的方式,构成重载的是C++的方式.

#include <iostream>
using namespace std;

#ifdef __cplusplus
extern "C"
{
#endif 
	void func(int a, int b)
	{
		printf("C : %d %d\n", a, b);
	}

#ifdef __cplusplus
}
#endif 

void func(double a, int b)
{
	printf("C++: %lf %d\n", a, b);
}

int main()
{
	func(1, 2);
	func(1.11, 2);
	return 0;
}

到此这篇关于C++ 函数重载背后的原理的文章就介绍到这了,更多相关C++ 函数重载内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C++ 函数重载背后的原理

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++ 重载函数的声明:理解函数签名重用的原理

重载函数允许在同一作用域内创建具有相同名称但不同参数列表的多个函数,从而实现代码重用和灵活性:函数签名包含函数名称和参数列表,用于唯一标识函数。参数列表可以包含基本数据类型、类类型、引用类型和指针类型。编译器根据实际参数匹配最佳匹配的函数签
C++ 重载函数的声明:理解函数签名重用的原理
2024-05-02

C++ 函数模板中函数重载的实现原理?

在 c++++ 函数模板中,函数重载可以通过编译器生成不同的符号名称和代码生成来实现。编译器根据传入的参数类型进行匹配,选择最匹配的重载。例如,模板中定义了 print(t) 和 print(t, u),实际调用时传入参数类型为 int 和
C++ 函数模板中函数重载的实现原理?
2024-04-15

C++ 函数模板详解:揭秘泛型算法背后的原理

回答: 函数模板用于编写可用于不同类型数据的通用函数,增强代码可重用性和效率。详细描述:语法: template returntype func++tionname(t arg1, t arg2, ...) { ... }原理: 利用编译
C++ 函数模板详解:揭秘泛型算法背后的原理
2024-04-27

C++ 函数重载中重载函数的规则是什么?

c++++ 重载函数的规则如下:1. 参数列表不同(数量、类型或顺序);2. 参数数量必须不同;3. 参数类型不同;4. 参数顺序不同;5. const 和引用限定符不影响重载。C++ 函数重载中重载函数的规则函数重载是 C++ 语言中一
C++ 函数重载中重载函数的规则是什么?
2024-04-14

C++ 函数重载和重写的理解和使用

c++++ 中函数重载允许在同一类中定义同名函数,但参数列表不同;函数重写发生在子类中定义一个与父类同名且参数相同的函数,子类函数将覆盖父类函数。实战示例中,重载函数用于针对不同数据类型执行加法运算,重写函数用于覆盖父类中的虚函数,以计算不
C++ 函数重载和重写的理解和使用
2024-04-20

C++重载的奥义之函数重载详解

函数重载是C++多态(静态多态)的特征体现,它可以允许重复使用同一个函数名(篮子)的函数,但是函数的参数列表(篮子装的东西)是可以不一样的。下面就简单讲讲C++中函数重载的相关应用吧
2023-05-16

C++ 中的函数重载和函数覆盖

c++++ 中,函数重载允许创建具有相同名称但不同参数列表的函数。函数覆盖发生在派生类中,派生类中定义的函数与基类中同名函数覆盖基类的方法。C++ 中的函数重载和函数覆盖引言函数重载和函数覆盖是 C++ 中两个重要的特性,它们允许以不同
C++ 中的函数重载和函数覆盖
2024-04-14

关于C++的重载运算符和重载函数

一般来说,重载运算符在实际的项目开发中会经常的用到,但如果某些自定义类型通过简短几行代码重载一些常用的运算符(如:+-*/),就能让编程工作带来方便,需要的朋友可以参考下本文
2023-05-19

C++ 函数的重载和覆盖

c++++ 中重载和覆盖是不同的概念。重载允许创建同名函数,具有不同的参数列表,而覆盖允许派生类函数覆盖基类同名函数。在重载中,函数名相同,但参数列表不同,在覆盖中,函数名和参数列表必须相同,并且派生类函数必须使用 override 关键字
C++ 函数的重载和覆盖
2024-04-12

C++中函数重载定义与原因是什么

小编给大家分享一下C++中函数重载定义与原因是什么,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!引例如果要求你只能通过print函数,即能打印字符串,又能打印一个整型。虽然在C语言中我们可以通过 print_i 和prin
2023-06-29

C++ 函数重载和函数模板的区别

函数重载和函数模板的区别:函数重载:具有相同名称但不同的输入类型和数量的同域函数,编译时根据输入类型选用相应函数。函数模板:通用的函数定义,使用类型占位符,在实例化时根据输入类型生成具体函数。C++ 函数重载和函数模板的区别函数重载函数重
C++ 函数重载和函数模板的区别
2024-04-14

C++ 函数命名:如何处理重载函数和同名函数

重载函数和同名函数的命名规则不同。重载函数通过区分参数类型来命名,而同名函数通过命名空间来分组。实战案例:重载字符串比较函数使用不同的参数类型进行区分,而不同命名空间的 abs() 函数则使用命名空间缩写或前缀来分组。为了避免命名冲突,建议
C++ 函数命名:如何处理重载函数和同名函数
2024-05-03

C++ 函数重载和重写与类层次结构的设计原则

函数重载和重写简介c++++ 中的函数重载和重写允许同名函数具有不同的参数列表或覆盖基类中的同名函数,以实现更灵活和可扩展的代码,并遵循重要原则(如 srp、lsp、dip)。C++ 函数重载、重写与类层次结构设计原则简介C++ 中的函
C++ 函数重载和重写与类层次结构的设计原则
2024-04-20

c++函数的重载怎么实现

这篇文章主要讲解了“c++函数的重载怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“c++函数的重载怎么实现”吧!1.函数重载概述作用:函数可以相同,提高复用性函数重载必须满足的条件:
2023-06-29

C++ 函数重载的最佳实践

c++++ 函数重载最佳实践:1、使用清晰且有意义的名称;2、避免过载过多;3、考虑默认参数;4、保持参数顺序一致;5、使用 sfinae。C++ 函数重载的最佳实践函数重载允许我们在 C++ 中创建具有相同名称但参数不同的多个函数。这为
C++ 函数重载的最佳实践
2024-04-20

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录