c++函数重载实现原理浅析(代码片段)

善良超锅锅 善良超锅锅     2022-10-20     331

关键词:

---------------------------------------------------献给所有和我一样还没拿到office的同学-----------------------------------------------------------------------------------                                     

                                                           C++函数重载实现原理浅析

 

       C++实现函数重载的技术手段是函数符号改名,所以我们可以通过分析编译器的函数符号改名机制来验证C++函数重载规则。



 

1.函数重载的概念
          函数重载: 出现在相同作用域中的多个函数,具有相同的名字而形参表不同。   注意:不能仅仅基于不同的返回类型而实现函数重载。返回值是不影响函数签名的。



2.函数调用:
          函数调用时会发生什么?学过8086汇编时,我们都知道函数调用是程序执行点跳转到一个符号所在的地方转而执行符号所在地址的代码,然后再跳回去。这个符号就是函数。     我们用一个简单的例子来说明一下函数调用 //在这个简单的实例中,我们只是简单的在main函数中调用了一下printhello函数来打印hello world!
#include <stdio.h>
void printhello()
        
   printf("hello world!\\n");


int main()

        printhello();//这里调用函数printhello
        return 0;
//函数调用部分对应的汇编代码为:
      main:.LFB1:
        .cfi_startproc
        pushq        %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq        %rsp, %rbp
        .cfi_def_cfa_register 6
        movl        $0, %eax
        call        printhello            ;这里call  printhello,跳转到符号printhello出执行      

        movl        $0, %eax
        popq        %rbp
        .cfi_def_cfa 7, 8
C语言中函数符号名和对应的函数名是一样的,而C++为了支持函数重载,符号名是在对应的函数名上改编的。如下图所示,函数名为func,而对应的符号名为_Z4funcv。

     



 

3. C++的函数符号命名规则

          在前面的的图示中,我们给出了C++函数编译符号实例,貌似函数名是对应符号的子串额。实际上函数的编译符号是根据函数名,函数的参数表(包括参数类型和数量)相关的。而且不同的编译器的命名规则不一样。只要能保证相同的函数名和不同的函数参数列表生成的符号名不一样就行。下面我们来感受一下GCC的C++编译器的命名规则。
3.1函数返回类型不影响生成的符号名
    前面我们说不能仅仅基于不同的返回类型而实现函数重载,原因是函数返回值并不影响最后生成的符号。我现在就验证一下:     我们分别在两个cpp文件中定义两个同名但返回值不同的函数,看一看他们在汇编代码中的符号是否一样。               
    第一个函数返回类型为void,生成的符号名为:_Z4funcv
            
          第二个函数名也为func,但返回类型为int,生成的符号名还是为:_Z4funcv

    上面两个同名但返回值类型不同函数生成相同符号名,说明返回类型是不影响符号名的。如果你定义两个函数,只是返回类型不同,那么它们生成的符号一样,肯定会发生符号重定义错误。


3.2 函数名,参数列表(参数类型、数目)才是影响符号名的因数
    下面我们观察多个参数列表不同的同名函数,看看它们对应的符号名是什么。这次我们不直接观察汇编代码(汇编代码太长了),而是用objdump -t命令直接观察代码对应的目标文件中的符号表。
假设有下面的这些函数(左),以及它们生成符号(右)  
     看来改编的符号名是在函数名前加了一个前缀,如果没有参数就在后面加一个字母v,如果是int参数就加一个i,如果是char参数就加一个c,float参数就加一个f,double参数加一个d。引用加R,指针加P。貌似我们找到了某种规则。不过不同的规律改编的方法不一样,我们没必要在意某个编译器使用的改名规则。只需要知道函数名+参数列表决定了符号名 就行。也可以看到第二个函数的int返回类型并没对函数的符号名有什么影响。


3.3 const形参对函数的符号名有影响吗?
3.3.1 第1组实验:     理论上const int  a和int a是不同类型的变量,那const对函数对应的符号名有影响吗?     我们用下面的代码测试一下:             
    哦,func1和func2形参只是一个有const限定,一个木有。它们出现了重定义错误,说明它们对应的符号是一样的,看来const对符号名木有影响啊,加或不加都一样。真的是这样吗? 我们再来测两组。
3.3.2 第2,3两组实验:             
    可以看出和前面一组实验不一样,这次的两组函数虽然参数只是一个有cosnt限定,一个没const限定,生成的符号名却都不一样,能通过编译。这说明了什么?(看到&和*没)       实际上仅当形参是引用或指针时,const形参才对符号名有影响。(实际上我也是在Primer书上看到的,这里只是验证一下,要不然我的脑壳可想不到)       注意如果形参本身是const指针不是这种情况(这和第一组实验类型)


3.3.3 第4,5组实验:             
    注意const int*和int *const的区别。实际上int & const不存在,因为一个引用绑定到一个对象后,不可能再绑定到另一个对象。所以int & const在语法中是不需要存在的。
      背景知识补充:常量指针(const int*或者int const*)与指针常量(int * const)。      常量指针是指不可通过指针给该指针指向的变量赋值(即不可以修改该变量),但是可以改变该指针的指向。定义函数时,如果不想在函数总修改所指向的参数,可以把形参声明问常量指针。C/C++标准库函数就是这么做的。      指针常量是指不可以改变指针的指向,但能通过指针给该指针指向的变量赋值(即可以修改该变量)。
   


4. 这也是extern  “C”的由来      分析到了这里,我们已经验证为了支持函数重载C++编译器的函数符号命名机制和C语言是不一样的。实际上C++的符号命名机制也适合全局变量。 所以然,如果你在C++中直接调用C语言编译的函数,链接时会找不到符号,发送符号未定义错误(undefined reference to之类的错误)。下面验证一下: 我们在文件cfunctest.h中声明了两个函数,并在8.c中实现了这两个函数,然后用C语言编译器编译8.c生成目标文件8.1.o。然后在8.cpp调用这个两个函数。先用8.cpp生成8.2.o。然后尝试将8.1.o8.2.o 链接
   我们来编译并链接一下  
          可以看出虽然8.c8.cpp虽然可以各自编译成功,但是链接到一起时候却链接不到要调用的函数符号。

     我们来看看这两个函数在8.1.o8.2.o中的符号各是什么:       可以看到两个函数在8.1.o8.2.o中的符号名是不同的。当然链接不上啦。如果查看8.c8.cpp对应的汇编代码,也会发现生成的符号是不同的。       如果我硬是要在C++代码中调用用C编译器编译的函数,那该怎么办呢?这时候该extern “C”登场了。之所以会出现链接错误,是因为C++在调用函数时候,把函数符号改名了,而且这种改名机制和C编译器的符号命名机制是不同的。所以我们要告诉C++编译器,在调用某个用C编译的函数时,不要用C++的符号命名机制,而是用C语言的符号命名机制。这就是extern “C”的功能。       把8.cpp#include “cfunctest.h”改成extern “C”#include “cfunctest.h”再试一下就行了。  

      在cpp文件中加上extern “C”后,重新编译,发现生成的目标文件8.2.o中函数的符号名和前一张图中8.1.o中的符号名相同了。链接也无错误了。 实际上你也可以直接在cpp文件把对应函数调用处的汇编代码call  _Z6cfunc1v改成call  cfuncv1call  _Z6cfunc2v改成call  cfunc2(反正保证两个目标文件中的函数符号一样就行)再用这个汇编代码编译,也可以正确链接的。

   
至于C++重载函数的匹配规律,有那么一点点复杂。看书就好。我这里只做这些分析了。   5.参考资料:《C++ Primer中文版》第四版 第7.8节:重载函数

 

                                                   

                                             

                                                       善良超哥哥的吐血之作,转载请注明出处:http://blog.csdn.net/candcplusplus/article/details/12746975

重载重写隐藏

1.重载:在相同作用域中,同名函数但参数列表不同,编译器将参数列表同函数名拼接在一起形成不同的符号,所以说重载必须参数列表不同,而不考虑返回类型。C++函数重载实现原理浅析。2.重写:父类虚函数与子类函数同名... 查看详情

c++多态(代码片段)

文章目录一.多态的概念二.多态的定义和实现(1).虚函数(2).虚函数的重写(3).多态的构成条件(4).override/final(5).重载/隐藏/重写的区别三.抽象类四.多态的原理(1).虚函数表(2).单继承中的虚函数表(3).多继承中的虚函数表一.多态的概念... 查看详情

c++多态(代码片段)

文章目录多态的概念多态的定义及实现多态的构成条件虚函数虚函数的重写虚函数重写的两个例外C++11override和final重载、覆盖(重写)、隐藏(重定义)的对比抽象类概念接口继承和实现继承多态的原理虚函数表多态的原理动态... 查看详情

c++多态(代码片段)

...一、多态的概念二、多态的定义及实现多态的构成条件虚函数虚函数的重写C++11override和final重载、覆盖(重写)、隐藏(重定义)的对比三、抽象类接口继承和实现继承四、多态的原理虚函数表多态的原理动态绑定与静态绑定五... 查看详情

c++多态详解(代码片段)

多态概念定义和实现多态的构成条件虚函数及其重写虚函数重写条件的两个例外C++11关键字override与final重载、重定义(隐藏)、重写(覆盖)的对比抽象类概念接口继承与实现继承原理虚函数表底层原理静... 查看详情

c++模拟实现string类(代码片段)

目录string类简介模拟实现成员变量成员函数构造函数拷贝构造赋值重载析构函数迭代器普通迭代器const迭代器重载运算符[]几种常见函数reserve()resize()push_back()append()重载+=insert()erase()find()操作符重载流插入<<流提取>>... 查看详情

c++——类的模拟实现(代码片段)

文章目录string类接口总览默认成员函数构造函数拷贝构造函数赋值运算符重载函数析构函数迭代器相关函数begin和end容量相关函数size和capacityreserve和resizeempty修改字符串相关函数push_backappendoperator+=inserteraseclearswapc_str访问字... 查看详情

c++多态性总结(代码片段)

...的对象接受时导致不同的行为。所谓消息是指对类的成员函数的调用,不同的行为是指不同的实现,也就调用不同的函数。换言之,多态指的就是用同样的接口访问功能不同的函数,从而实现“一个接口,多种方法”。二,多态... 查看详情

实验2:函数重载函数模板简单类的定义和实现(代码片段)

实验目的   1.掌握c++中函数的声明、定义、调用和参数传递方式2.掌握c++中带有默认形参值的函数声明和定义方法3.理解函数重载,掌握c++中函数重载的实现方式4.理解函数模板,掌握c++中函数模板的简单使用5.理解面... 查看详情

c++类和对象-日期类的实现(代码片段)

...类的简单实现帮助我们完整复习一遍类的“六大默认成员函数”。文章目录1.日期类的定义2.日期类成员函数具体实现2.1准确获取某年某月有多少天2.2日期类构造函数2.3日期类拷贝构造2.4赋值运算符重载2.5+=运算符重载2.6+... 查看详情

c++类和对象-日期类的实现(代码片段)

...类的简单实现帮助我们完整复习一遍类的“六大默认成员函数”。文章目录1.日期类的定义2.日期类成员函数具体实现2.1准确获取某年某月有多少天2.2日期类构造函数2.3日期类拷贝构造2.4赋值运算符重载2.5+=运算符重载2.6+... 查看详情

c++——list的模拟实现(代码片段)

...实现list迭代器的模拟实现迭代器类的模板参数说明构造函数++运算符的重载--运算符的重载==运算符的重载!=运算符的重载*运算符的重载->运算符的重载list的模拟实现默认成员函数构造函数拷贝构造函数赋值运算... 查看详情

c++中的多态和虚函数及多态原理(代码片段)

...分类多态分为静态和动态多态静态多态包括运算符重载和函数重载,复用函数名;动态多态包括虚函数派生类;区别:静态多态地址在编译期已经确定函数地址;动态多态函数地址在运行是确定;虚函数成... 查看详情

c++入门(代码片段)

...#43;+的输入和输出缺省参数缺省参数概念缺省参数分类函数重载函数重载概念函数重载的原理extern“C”引用引用概念引用特性常引用引用的使用场景传值、传引用效率比较引用和指针的区别内联函数特性auto关键字auto简介auto... 查看详情

c++入门,初篇(代码片段)

...省参数的意义2.C++缺省参数的分类五、C++中的函数重载1.函数重载的定义2.函数重载的实现六、C++中的引用1.引用的实现2.引用和指针的比较六、内联函数总 查看详情

c++之类和对象(代码片段)

文章目录类和对象(二)构造函数构造函数概念构造函数特征析构函数析构函数概念析构函数特征拷贝构造函数拷贝构造函数概念拷贝函数特征运算符重载函数日期类的实现日期类的运算符重载函数const成员函数const修饰类的成员函... 查看详情

[c++]多态详解(代码片段)

...重定义的对比4.抽象类5.多态的原理5.1问题提出5.1.1包含虚函数的类和不包含虚函数的类有什么区别?5.2.2派生类与基类的虚表是否相同?5.2虚函数表5.2.1虚函数表概念5.2.2虚函数表的构建规则6.多态原理6.1动态绑定与静态绑... 查看详情

c++函数重载,函数模板和函数模板重载,选择哪一个?(代码片段)

重载解析  在C++中,对于函数重载、函数模板和函数模板重载,C++需要有一个良好的策略,去选择调用哪一个函数定义(尤其是多个参数时),这个过程称为重载解析。  (这个过程将会非常复杂,但愿不要遇到一定要写这种... 查看详情