【C++入门】函数重载


1 函数重载概念

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

示例:

  • 参数类型不同
//int型参数
int Add(int num1, int num2)
{
    
    
	cout << "int Add(int num1, int num2)" << endl;
	return num1 + num2;
}

//double型参数
double Add(double num1, double num2)
{
    
    
	cout << "double Add(double num1, double num2)" << endl;
	return num1 + num2;
}
  • 参数个数不同
//无参数
void Func() {
    
    
	cout << "Func()" << endl;
}

//有一个参数a
void Func(int a) {
    
    
	cout << "Func(int a)" << endl;
}
  • 参数类型顺序不同
//正确方式
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;
}

//错误方式
void F(int a, int b)
{
    
    
	cout << "F(int a,int b)" << endl;
}
void F(int b, int a)
{
    
    
	cout << "F(int b, int a)" << endl;
}

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

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

程序编译过程

程序编译过程

  • 实际项目通常是由多个头文件和多个源文件构成,通过C语言中的编译链接,我们可以知道,当a.cpp中调用了b.cpp中定义的Add函数时,在编译后链接前,目标文件a.o中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在目标文件b.o中,那该怎么办呢?
  • 链接阶段就是专门处理这种问题的,链接器看到a.o调用了Add,但是没有Add的地址,就会到b.o的符号表中找Add的地址,然后链接到一起。
  • 那么链接时,面对Add函数,链接器会使用哪个名字去找呢?需要注意的是:每个编译器都有自己的函数名修饰规则。
  • 由于Windows下vs的修饰规则过于复杂,而Linux下g++的修饰规则简单易懂,下面使用g++演示修饰后的名字(示例):

    ① 采用C语言编译器编译后的结果: 可以看出在Linux下采用C语言编译器编译后,函数名修饰后的名字不变(也因此可以理解C语言无法支持函数重载)
    函数名修饰示例

    ② 采用C++编译器编译后的结果: 在Linux下采用C++编译器编译后,函数名经过修饰后发生了改变,编译器将函数参数类型信息添加到修改后的名字中。修饰规则为:_Z + 原函数名长度 + 原函数名 + 类型首字母(指针则为P + 指向数据类型首字母)。
    C++函数名修饰示例


    ③ Windows下函数名修饰规则

    示例:如下,当我们只进行函数声明而没有进行函数定义,此时调用函数会发生报错,可以看到图中报错信息中的 ?func@@YAHH@Z 即为函数 int func(int a) 修饰后的名称。
    Windows下函数名修饰示例
    Windows下的一些函数名修饰举例:
函数声明 修饰后的函数名
int func(int) ?func@@YAHH@Z
float func(float) ?func@@YAMM@Z
int C::func(int) ?func@C@@AAEHH@Z
int C::C2::func(int) ?func@C2@C@@AAEHH@Z
int N::func(int) ?func@N@@YAHH@Z
int N::C::func(int) ?func@C@N@@AAEHH@Z

我们以 int N::C::func(int) 这个函数声明来猜测Visual C++的函数名修饰规则。修饰后名字由 ? 开头,接着是函数名,由 @ 符号结尾;后面跟着由 @ 结尾的类名 C 和空间名称 N ,再一个 @ 表示函数的名称空间结束;第一个 A 表示函数调用类型为 __cdecl ,接着是函数的参数类型及返回值,由 @ 结束,最后由 Z 结尾。可以看到函数名、参数类型和名称空间都被加入了修饰后名称,这样编译器和链接器就可以区别同名但不同参数类型或命名空间的函数,而不会导致链接的时候函数多重定义。

④ 如果两个函数的函数名和参数列表是一样的,只是返回值类型不同是不构成重载的,因为调用时编译器没办法区分(即使在修饰后的名称中加入返回值类型,但我们在调用函数时并不会加上返回值类型)。


以上是我对C++中函数重载的一些学习记录总结,如有错误,希望大家帮忙指正,也欢迎大家给予建议和讨论,谢谢!

猜你喜欢

转载自blog.csdn.net/qq_67216978/article/details/128809693