C++——深入探究函数重载

文章目录

  • 概述
  • 函数重载
  • 函数重载的概念
    • 函数重载的细节
  • C++支持函数重载的原理——名字修饰(name Mangling)

概述

本篇博客讲诉的是c++函数重载是什么,以及了解其种的一些特征以及重载函数的意义,并且运用linux中的g++编译器简单探究一下函数重载底层是如何实现的。

函数重载

首先,我们先从自然语言的角度来认识一下函数重载,在地然语言中,一个词可以有多重含义,简称一词多义,人们可以通过上下文来判断该词的具体含义,一词多义正是函数重载的关键内涵。

函数重载的概念

函数重载:是函数的一种特殊情况,c++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数,类型或者类型顺序)不同,常用来处理实现功能相近数据类型不同的问题,可以说这一方面是c++对c语言的重要改进之一。

我们通过几个例子来初步了解一下函数重载:

#include
using namespace std;

//1.参数类型不同
int add(int a, int b)
{
	cout << "add(int a, int b)" << endl;
	return a + b;
}
double add(double a, double b)
{
	cout << "add(double a, double b)" << endl;
	return a + b;
}

//2.参数个数不同
void func()
{
	cout << "func" << endl;
}
void func(int a)
{
	cout << "func(int a)" << endl;
}

//3.参数类型顺序不同
//(其实实际意思就是参数类型不同)
void f(int a, char b)
{
	cout << "f(int,char)" << endl;

}
void f(char a, int b)
{
	cout << "f(char,int)" << endl;
}

int main()
{
	add(10, 10);
	add(1.1, 9.8);

	func();
	func(29);

	f(10, 'a');
	f('a', 10);

	return 0;
}

从上面这串代码就可以很容易的看到函数重载的强大功能了把!但是,关于函数重载,还有一些需要注意的点。

函数重载的细节

  1. 看下面一段代码:
void func(int a, char b)
{
	using namespace std;
	cout<<"func(int a,char b)"<<endl;
}
void func(int b, char a)
{
	using namespace std;
	cout<<"func(int b, char a)"<<endl;
}

上面的代码参数顺序和类型都相同,但是形参的名字不同,构成重载吗?

如果大家对编程有一定基础,都能很容易明白,这肯定不是函数重载,形参的名字如何取,只要上面提到的形参类型,顺序,数量完全相同,就肯定不构成重载!

其实还有一种方法很好理解,如果上面的函数定义构成重载,那么如果调用
func(10,'x')
编译器能知道应该调用哪个函数吗?显然是不行的!

  1. 这也是函数重载的第二个大坑
    参数列表完全相同,返回值不同的函数能构成函数重载吗?
    如下:
int f()
{
	int tmp = 10;
	return tmp;
}
double f()
{
	int tmp = 1.1;
	return tmp;
}

这两个函数能构成重载吗?显然也是不构成!为什么呢?
其实原因也很简单,假设两个函数构成重载,能够编译通过,函数调用时,指令如下:
f();
通过这个函数调用,编译器仍然不能知到函数应该调用哪个函数,所以只有返回值不同是完全不构成重载的!

C++支持函数重载的原理——名字修饰(name Mangling)

大家是不是都有一个疑惑,cpp是如何实现函数重载的呢?换个方式问,就是cpp为什么支持函数重载,而c语言不支持呢?

这里就需要我们去简单地深入探究一下底层实现了。

下面部分可能需要一点编译链接的知识,下面是一篇作者简单探究c语言从源文件到可执行程序的过程博客,对编译链接没有了解的读者可以先看看链接的博客再食用该博客效果更佳!(doge
链接:C语言程序环境剖析——探究从.c到.exe之路

在c/cpp中,一个程序要运行起来,需要经历以下几个阶段:预处理,编译,汇编,链接

a.h a.cpp text.cpp

  • 预处理:头文件展开/宏替换/条件编译/去掉注释……(预处理完成之后生成.i后缀文件)
    a.i test.i
  • 编译: 检查语法,生成汇编代码
    a.s test.s
  • 汇编: 汇编代码转换成机器码

注意,这里的汇编汇编语言不是同一个东西

  • 链接:将多个头文件链接生成可执行程序

windows:xxx.exe/linux:默认a.out

其中编译和链接过程中有一个在这个部分非常重要的点就是生成符号表符号表汇总,关于符号表的具体知识在上面哪篇博客中有具体的讲解,简单来说就是符号(函数名,全局变量等等)+地址


c语言中函数符号表示:
这里在centos系统下写了一个test.c文档,内容如下:

#include
int func(int a, double d)
{
	printf("func(int a, double d)\n");
}

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

然后用linux中的gcc编译器输入
gcc -o testc test.c生成一个编译后的目标文件。

然后使用指令
objdump -S testc查看生成的函数的代码:

C++——深入探究函数重载_第1张图片
可以发现在c语言的编译下函数符号就直接由函数名表示。

同样的方法,我们再写一个cpp的文件,内容如下:

int func(int a)
{
	printf("func(int)");
}
int func(int a, double d)
{
	printf("func(int, double)");
}
int main()
{
	func(1);
	func(1,1.1);
	return 0;
}

换用g++编译器进行编译,指令如下:
g++ -o testcpp test.cpp

注:这里-o 后面的testcpp是在指定生成目标文件的文件名

再用同样的方法查看重载后两个函数的符号表示:
C++——深入探究函数重载_第2张图片

可以看到,cpp中对函数符号做了一定修饰,可以大致看出符号中不止有函数名,例如第一个参数在func后面还加入了i,前面加上了_Z4,而第二个func后面加上了id,这里就可以大致推理,cpp在形成符号表时对函数名进行了修饰,后面的id是int和double的缩写,这也是cpp能分辨出重载函数的原因,这个过程也叫做符号修饰

注意:并不是所有编译器上的符号修饰规则都一样!

下面是windows系统下vs编辑器的符号修饰规则图:
C++——深入探究函数重载_第3张图片

可以看到,vs编辑器对符号修饰的规则制定的还是比较复杂的,没有像g++编辑器浅显易懂。


以上就是关于C++函数重载的内容了,如果博主哪里写的有问题或者有疑惑还请大家在评论区指出
C++——深入探究函数重载_第4张图片

你可能感兴趣的:(C++,c++,开发语言)