C++中函数重载的原理

C++的编译器在编译函数时,会对函数进行换名,将参数的类型信息整合到新的名字中,解决函数重载和名字冲突的矛盾。

在C++标准语法规定,在编译C++函数时候,会进行换名,将函数的参数表类型信息整合到新的名字中,因为满足多个重载函数的多个函数参数有所差异,这样在换名字之后他所得到的新的名字也是有所差异的 ,通过这样的语法规则,来解决函数重载和名字冲突的矛盾

c++ 复制代码
//test.c
void func(int i,double d){}

/*现在分别使用C编
译器和C++编译器来
编译test.c源文件,
查看二者差异。*/

gcc -C test.c -o 1.o
    g++ -C test.C -o 2.o
    nm//可以查看一个目标文件中的一些标识符
    nm 1.o
    00000000 T func//gcc 编译后目标文件中的函数名字并没有改变
    nm 2.0
    00000000 T _Z4funcid
    /*经过C++编译器g++编
    译后的源文件中的函数
    名字被改变了,变为
    了"_Z4funcid",其中"_Z"是
    编译器的内置标识,并
    没有什么具体的含义,我
    们可以忽略它,"4"表示的
    应该是函数名字的长
    度(func正好是4个字符),
    "i"表示int,"d"表示double.
    因为此时源文件test.c中
    的函数func(int i,double d)的
    参数是一个int类型和
    一个double类型。换名就
    是将函数的参数表的类型
    信息给整合到新的名字中。
    这样假设在一个文件当中
    定义了重载关系的多个函
    数,经过换名以后,因为
    它的参数有差别,因此换
    名后的名字也有所差异,
    这时候再进行链接的话,不
    会有重定义的错误。*/
c++ 复制代码
//test.c
  void func(int i, double d){}
 void func(int i, int j){}

/*此时test.c中的函
数构成重载关系
(相同作用域;相
同函数名;不同参
数)。此时再次使用
gcc编译器编译,会
报重定义的错误,但
是使用g++编译器编
译却可以编译通过,
通过nm命令查看
可以发现新换的
名字的函数名字
是有所差异的,因
为名字不同,所以
编译时不会出现重
定义错误。这就是
函数重载原理。*/

g++ -C test.c
nm test.o
00000000 T _Z4funcid
00000015 T _z4funcii

函数重载的原理不重要,因为实际开发中,知道什么是函数重载,知道函数重载的应用的语法就可以,但还是要知其然更要知其所以然。

在C++中,extern "C" 声明了它的一个作用,这和C编程与C++混合编程是有关联的。
exter "C"声明的作用:
(1)可以在C++函数前面加extern"C"声明,强制要求C++编译器按照C的风格去编译函数,不要进行换名,方便C程序去调用。
(2)被extern "C"声明的函数无法重载

void func(int i){...}//编译之后:_Z4funci
extern "C" void func(int i){...}//编译之后:func

假设现在有个一工程大型的项目.

mkdir 03project

在实际开发中经常会涉及到C和C++的混合编程,现在来模拟这样一个工程,用纯C语言或者纯C++语言来实现都不是很合适,想采用混编的形式,有的地方追求的是效率,用C语言编程更方便点,有的地方使用C++实现则更省事,这样在当前的工程中有C语言的文件也有C++的源文件。

touch c.c

touch cpp.cpp

c++ 复制代码
 //vim cpp.cpp
 
    /*C++的封装
    和继承度应该更
    好点,面向对象
    编程,使用C++写
    一些逻辑更为复
    杂的程序比较合适*/
    
#include <iostream>
using namespace std;

void hello(void){
    cout<< "hello c++!"<<endl;
}
C++ 复制代码
//vim c.c

/*通常是将函数
的声明写到头文
件中,再通过include包
含此头文件,include声
明的作用就是将头文件
中声明的东西原样地
拷贝到当前文件中,此
处为了示意方便就不写
头文件了,直接将函数声
明放在这里。*/
void hello(void);

int main(void){
    hello();//主函数中直接调用C++源文件中的函数
    return 0;
}

将两个文件链接
ls 
c.c   cpp.cpp
//这两个文件分开分别编译
g++ -C cpp.cpp //只编译不链接
gcc -C c.c//只编译不链接
//编译后就得到了以".o"结尾的目标文件
ls
c.c c.o  cpp.cpp cpp.o

/*现在需要将两个目标文件链接起来,
链接使用g++或者gcc都可以,
但g++更方便点,不需要显式
链接标准c++库了*/
g++ cpp.o c.o

/*此时会报错,
报错信息如下,
就是找不到,不知
道定义在哪个位置,
我们知道我们已经
在cpp.cpp中定义了,
为什么说找不到呢?
这就是函数换名的原因啦
*/
c.o: 在函数'main'中:
c.c:(.test+0x12):对'hello'未定义的引用
collect2:error: ld returned 1 exit status

//查看cpp.o
nm cpp.o
00000000 T _Z5hellov
/*可以看出它换名字了,
所以在C的源文件中
调用hello()函数时候找
不到"hello"函数名了。
那该如何解决呢?*/

方法1:在C程序中调用被C++编译器更换的原C++源文件中的函数的名字
修改C语言的源文件,调用换名字后的函数,原来的名字已经不存在了,经过C++编译器编译原来的名字已经被更换了

C++ 复制代码
//vim c.c

//void hello(void);
void _Zhellov(void);//声明更换名字后的函数
int main(void)
{
    //hello();
    _Z5hellov();//调用更换名字后的函数
    return 0;
}

//此时再次重新编译链接就可以成功
gcc -C c.c
g++ c.o cpp.o//可以成功
./a.out //也可以得到正常的想要的结果
hello C++!
    
    /*这样子做这
    种C语言和C++混合
    编程的问题可以被解
    决,但是这种解决问
    题的方法不好,因为
    此时C源文件中掉用的
    式更换名字后的函数
    ,如果自己是一名
    程序维护者,自己
    肯定想要看看这个
    被调用的函数的实
    现是如何做的,但是
    此时找遍整个函数
    的定义和声明根本
    找不到"void _Z5hellov(void)"类
    似这种被换过名字
    的函数的定义和声明,
    这种被换过名字的函数
    直接写进去的话代码可
    读性十分差劲,还有就
    是因为在标准的C++中没
    有特别详细的关于换名字
    的规定规则,标准C++只
    是规定了要把函数的参数
    类型表信息整合到新的名
    字当中,但是它并没有
    严格说明具体是怎么整
    合的,比如:表达这个
    void类型的参数,当前
    的GUN的编译器是加
    了一个"v",但是若是换
    成微软的编译器可
    能是加"void"全拼或者
    其他的形式,这种整合
    的形式有所差异,这样
    的代码就不具备跨平台
    的特性,所以这种做法
    不好,需要另外的解决
    方法。解决方法如下方法2:*/

方法2:在C++源文件中添加:extern "C"

C++ 复制代码
  //vim c.c
    void hello(void);
 //void _Zhellov(void);//声明更换名字后的函数
int main(void)
{
    hello();
   // _Z5hellov();//调用更换名字后的函数
    return 0;
}


//vim cpp.cpp

#include <iostream>
using namespace std;

extern "C" void hello(void){
    cout <<"hello C++!"<<endl;
}

/*C++源文中的函数
直接编译函数会被
换名字,这样在C源
文件中调用时候会
出现未定义错误,因此
解决方法是在C++源文
件中定义函数的前面加
上:extern "C","C"是代
表C语言的意思,这时显
式的声明下,告诉C++编
译器,在编译这个函数时
候呢使用C语言的方式
进行编译,也就是告诉
编译器编译时候不要变
换函数名字了,这样做
的目的就是为了方便C
程序去调用C++程序。*/

gcc -C c.c
g++ -C cpp.cpp
g++ c.o cpp.o
./a.out
hello C++!
    /*可以看出能够
    正常编译了,
    正常链接了,
    经过g++编译
    的文件cpp.cpp,因
    为我们在函数前
    面添加了:extern "C",此
    时再看cpp.o中的表
    示符内容,函数的
    名字不会被更换修改.*/
    nm cpp
    00000000 T hello
    
    /*
    
    实际开发中,
    涉及到C和C++混
    合编程时候,
    在C++的源文件
    的函数定义的
    函数名字前面添
    加:extern "C",告
    诉C++编译器在编
    译时不要更换名字,
    方便C程序直接调用。
    */
    
    /*
extern "C" void hello(void){
    cout <<"hello C++!"<<endl;
}
像这样仅仅
是为了方便C
函数的调用,
他是无 法重载的
,函数换名是通过
重载来实现的,通过
extern "C "声明之
后,函数无法换名,
那么它也就无法重载了。
*/
相关推荐
唐诺3 小时前
几种广泛使用的 C++ 编译器
c++·编译器
冷眼看人间恩怨4 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
红龙创客4 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin4 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
yuanbenshidiaos6 小时前
c++---------数据类型
java·jvm·c++
十年一梦实验室6 小时前
【C++】sophus : sim_details.hpp 实现了矩阵函数 W、其导数,以及其逆 (十七)
开发语言·c++·线性代数·矩阵
taoyong0016 小时前
代码随想录算法训练营第十一天-239.滑动窗口最大值
c++·算法
这是我586 小时前
C++打小怪游戏
c++·其他·游戏·visual studio·小怪·大型·怪物
fpcc6 小时前
跟我学c++中级篇——C++中的缓存利用
c++·缓存
呆萌很7 小时前
C++ 集合 list 使用
c++