zoukankan      html  css  js  c++  java
  • C++中重载函数详解

    函数的重载详解

    什么时函数重载:

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

        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++调用

  • 相关阅读:
    【算法学习笔记】76.DFS 回溯检测 SJTU OJ 1229 mine
    【算法学习笔记】75. 动态规划 棋盘型 期望计算 1390 畅畅的牙签盒(改)
    【算法学习笔记】74. 枚举 状态压缩 填充方案 SJTU OJ 1391 畅畅的牙签袋(改)
    【算法学习笔记】73.数学规律题 SJTU OJ 1058 小M的机器人
    【算法学习笔记】72.LCS 最大公公子序列 动态规划 SJTU OJ 1065 小M的生物实验1
    【算法学习笔记】71.动态规划 双重条件 SJTU OJ 1124 我把助教团的平均智商拉低了
    【算法学习笔记】70.回文序列 动态规划 SJTU OJ 1066 小M家的牛们
    【算法学习笔记】69. 枚举法 字典序处理 SJTU OJ 1047 The Clocks
    【算法学习笔记】68.枚举 SJTU OJ 1272 写数游戏
    【算法学习笔记】67.状态压缩 DP SJTU OJ 1383 畅畅的牙签袋
  • 原文地址:https://www.cnblogs.com/cuckoo-/p/10789222.html
Copyright © 2011-2022 走看看