C++函数重载原理,学了链接才知道的

Aki 发布于 2022-11-30 256 次阅读


在实际的开发中,有时候我们需要实现几个功能类似的函数,只是有些细节不同。例如希望交换两个变量的值,但是这两个变量可能有多种类型:int、char、double、bool等。在C语言中,程序员往往需要分别设计出多个不同名的函数,但是在C++中,这完全没有必要。C++允许多个函数拥有相同的名字,只要它们的参数列表不同就可以,这就是函数的重载。借助函数重载,一个函数名就可以有多种用途。

C++函数重载的条件:

  • 1)函数名相同
  • 2)函数参数个数不同(即:参数个数不同/参数类型不同/参数顺序不同)

函数重载的实现原理、

  • C++代码在编译时会根据参数列表对函数名进行命重名(该技术被官方称为:name mangling),例如 void swap(int v1, int v2)会被重命名为 _swapii ,void swap(char v1,char v2)会被重命名为 _swapcc(不同的编译器会有不同的重命名规范,这里仅仅举例说明,实际情况可能并非如此)。当发生函数调用时,系统便会根据这些被重新命名的函数名去调用相应的函数。
  • 因此从这个角度来讲,函数重载仅仅是语法层面上的,本质上它们还是不同的函数,占用不同的内存,入口地址也不一样。

例如下面的这些重载函数:

#include<iostream>
using namespace std;
void func(int,int)
{
	
}


void  func(int)
{
	
}

void func(int,int,int)
{
	

void func(double,int)
{
	
}

void func(double,double)
{

}


int main()
{
	func(200);
	func(2,1);
	func(100,200,300);
	func(1.1,2.2);
	func(10.1,2);
	return 0;
}

我们编译后反汇编得到(g++,gdb):

   0x00000000004006ed <+0>:     push   %rbp
   0x00000000004006ee <+1>:     mov    %rsp,%rbp
   0x00000000004006f1 <+4>:     mov    $0xc8,%edi
   0x00000000004006f6 <+9>:     callq  0x4006b3 <_Z4funci>
   0x00000000004006fb <+14>:    mov    $0x1,%esi
   0x0000000000400700 <+19>:    mov    $0x2,%edi
   0x0000000000400705 <+24>:    callq  0x4006a6 <_Z4funcii>
   0x000000000040070a <+29>:    mov    $0x12c,%edx
   0x000000000040070f <+34>:    mov    $0xc8,%esi
   0x0000000000400714 <+39>:    mov    $0x64,%edi
   0x0000000000400719 <+44>:    callq  0x4006bd <_Z4funciii>
   0x000000000040071e <+49>:    movsd  0x11a(%rip),%xmm1        # 0x400840
   0x0000000000400726 <+57>:    movsd  0x11a(%rip),%xmm0        # 0x400848
   0x000000000040072e <+65>:    callq  0x4006dc <_Z4funcdd>
   0x0000000000400733 <+70>:    movsd  0x115(%rip),%xmm0        # 0x400850
   0x000000000040073b <+78>:    mov    $0x2,%edi
   0x0000000000400740 <+83>:    callq  0x4006cd <_Z4funcdi>
   0x0000000000400745 <+88>:    mov    $0x0,%eax
   0x000000000040074a <+93>:    pop    %rbp
   0x000000000040074b <+94>:    retq   

然后在查看符号表得到(readelf -s):

    86: 00000000004006dc    17 FUNC    GLOBAL DEFAULT   13 _Z4funcdd
    89: 00000000004006cd    15 FUNC    GLOBAL DEFAULT   13 _Z4funcdi
    92: 00000000004006bd    16 FUNC    GLOBAL DEFAULT   13 _Z4funciii
    94: 00000000004006b3    10 FUNC    GLOBAL DEFAULT   13 _Z4funci
   111: 00000000004006a6    13 FUNC    GLOBAL DEFAULT   13 _Z4funcii

发现了我们写的函数名称func被替换成了_Z4func开头的函数,这是因为在C++和C生成的符号表中,函数名的修饰规则是不同的,C++是函数名加参数类型,C就是函数名本身。也就是说C++中的同名函数重载,在底层来看其实是两个函数符号

最后我们的核心:C++之所以有重载,其原因是函数名修饰规则不同