函数的重载详解

什么时函数重载:

  函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。

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

 

 1 int Add(int left, int right)
2 {
3 return left+right;
4 }
5
6 double Add(double left, double right)
7 {
8 return left+right;
9 }
10
11 long Add(long left, long right)
12 {
13 return left+right;
14 }
15
16 int main()
17 {
18 Add(10, 20);
19 Add(10.0, 20.0);
20 Add(10L, 20L);
21
22 return 0;
23 }

例如这里定义了三个Add函数,传入的参数与顺序都相同,都是 left/ right ,但参数的类型却不同,有 int型,long型, double型,所以可以依靠这些类型的不同来区分要调运哪个函数。

在这里要特别注意:这里的参数必须是参数的 个数 或 类型 或 顺序不同,如果是返回值不同,则不属于函数重载,如:Add前的int, long, double。

下面就介绍一下在编译时具体的名字修饰规则:

2.名字修饰

  Name Mangling是一种在编译过程中,将函数、变量的名称重新改编的机制,简单来说就是编译器为了区分各 个函数,将函数通过某种算法,重新修饰为一个全局唯一的名称。
  C语言的名字修饰规则非常简单,只是在函数名字前面添加了下划线。比如,对于以下代码,在后链接时就 会出错:

1 int Add(int left, int right);
2
3 int main()
4 {
5 Add(1, 2);
6 return 0;
7 }

编译器报错:error LNK2019: 无法解析的外部符号 _Add,该符号在函数 _main 中被引用。

上述Add函数只给了声明没有给定义,因此在链接时就会报错,提示:在main函数中引用的Add函数找不到函数体。从报错结果中可以看到,C语言只是简单的在函数名前添加下划线。因此当工程中存在相同函数名的函数时,就会产生冲突。
由于C++要支持函数重载,命名空间等,使得其修饰规则比较复杂,不同编译器在底层的实现方式可能都有差异

1 int Add(int left, int right);
2 double Add(double left, double right);
3
4 int main()
5 {
6 Add(1, 2);
7 Add(1.0, 2.0);
8 return 0;
9 }

在vs下,对上述代码进行编译链接,后编译器报错:
error LNK2019: 无法解析的外部符号 "double cdecl Add(double,double)" (?Add@@YANNN@Z) error LNK2019: 无法解析的外部符号 "int __cdecl Add(int,int)" (?Add@@YAHHH@Z)

通过上述错误可以看出,编译器实际在底层使用的不是Add名字,而是被重新修饰过的一个比较复杂的名字, 被重新修饰后的名字中包含了:函数的名字以及参数类型。这就是为什么函数重载中几个同名函数要求其参数列表不同的原因。只要参数列表不同,编译器在编译时通过对函数名字进行重新修饰,将参数类型包含在终的名字中,就可保证名字在底层的全局唯一性。

比较c/c++中的名字修饰:

  a、C编译时函数名修饰约定规则:

  __stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,

  格式为_functionname@number。

  __cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为_functionname。

  __fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,

  格式为@functionname@number。

  它们均不改变输出函数名中的字符大小写,这和PASCAL调用约定不同,PASCAL约定输出的函数名无任何修饰且全部大写。

  b、C++编译时函数名修饰约定规则:

  __stdcall调用约定:

  1、以“?”标识函数名的开始,后跟函数名;

  2、函数名后面以“@@YG”标识参数表的开始,后跟参数表;

  3、参数表以代号表示:

  X--void ,

  D--char,

  E--unsigned char,

  F--short,

  H--int,

  I--unsigned int,

  J--long,

  K--unsigned long,

  M--float,

  N--double,

  _N--bool,

  ....

  PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代 表一次重复;

  4、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;

  5、参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。

  其格式为“?functionname@@YG*****@Z”或“?functionname@@YG*XZ”,例如

  int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z”

  void Test2() -----“?Test2@@YGXXZ”

  __cdecl调用约定:

  规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YA”。

  __fastcall调用约定:

  规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YI”。

  VC++对函数的省缺声明是"__cedcl",将只能被C/C++调用

C++中重载函数详解的更多相关文章

  1. JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解

    二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...

  2. 【转】linux 中fork()函数详解

    在看多线程的时候看到了这个函数,于是学习了下,下面文章写的通俗易懂,于是就开心的看完了,最后还是很愉快的算出了他最后一个问题. linux 中fork()函数详解 一.fork入门知识 一个进程,包括 ...

  3. Linux中fork()函数详解(转载)

    linux中fork()函数详解 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事, ...

  4. php中magic_quotes_gpc函数详解

    magic_quotes_gpc函数在php中的作用是判断解析用户提示的数据,如包括有:post.get.cookie过来的数据增加转义字符“\”,以确保这些数据不会引起程序,特别是数据库语句因为特殊 ...

  5. python unittest框架中addCleanup函数详解

    接上一篇doCleanups说明,这次介绍下另一个很好用的函数:addCleanup 还是老规矩,看官方文档说明: addCleanup(function, *args, **kwargs)¶ Add ...

  6. JavaScript基础内容中的函数详解

    函数 函数:即方法 函数就是一段预先设置的功能代码块,可以反复调用,根据输入参数的不同,返回不同的值. 为什么使用函数: 1.方便调用 2.代码重用,利于维护 3.便于修改,便于重构 4.简化逻辑,利 ...

  7. python中groupby函数详解(非常容易懂)

    一.groupby 能做什么? python中groupby函数主要的作用是进行数据的分组以及分组后地组内运算! 对于数据的分组和分组运算主要是指groupby函数的应用,具体函数的规则如下: df[ ...

  8. Linux中fork()函数详解

    一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程, 也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同 ...

  9. Linux C 中 fork() 函数详解

    一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork() 函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同 ...

随机推荐

  1. IntelliJ IDEA 2017.3 多模块右边栏 maven projects,maven项目命名问题

    我新建了一个maven web 模块,命名为cloud-access,可是install的时候,名字突然变为cloud-access Maven Webapp了,我就纳闷了,怎么回事.找了很久没发现原 ...

  2. [MyBatis]完整MyBatis CRUD工程

    下载地址:https://files.cnblogs.com/files/xiandedanteng/Person191005.rar pom.xml:这个文件主要是引入依赖 <project ...

  3. 前端知识点回顾之重点篇——ES6的Promise对象

    Promise Promise 是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理和更强大. 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异 ...

  4. Java:基础知识点

    1. 面向对象的特征 (1)封装:把过程和数据包围起来,对数据的访问只能通过已定义的界面.即现实世界可以被描绘成一系列完全自治.封装的对象,这些对象通过一个受保护的接口访问其他对象:(2)继承:是一种 ...

  5. Oracle存储过程、游标、函数

    SQL99是什么 (1)是操作所有关系型数据库的规则 (2)是第四代语言 (3)是一种结构化查询语言 (4)只需发出合法合理的命令,就有对应的结果显示 SQL的特点 (1)交互性强,非过程化 (2)数 ...

  6. DisplayUtils

    import android.app.Activity; import android.content.Context; import android.graphics.Rect; import an ...

  7. linux简单命令8---软件包安装

    1:使用yum安装,它不能包查询和包校验.它安装的是RPM格式文件.没有yum文件 ---------------------------------------------------------- ...

  8. k8s-高可用多主master配置

    准备主机 centos7镜像 node1: 192.168.0.101 node2: 192.168.0.102 node3: 192.168.0.103 vip: 192.168.0.104 配置s ...

  9. Python之网络模型与图形绘制工具networkx

    笔记 # https://www.jianshu.com/p/e543dc63454f import networkx as nx import matplotlib.pyplot as plt ## ...

  10. Springboot学习—CommandLineRunner接口(转载)

    前言 Spring boot的CommandLineRunner接口主要用于实现在应用初始化后,去执行一段代码块逻辑,这段初始化代码在整个应用生命周期内只会执行一次. 如何使用CommandLineR ...