C++深入浅出讲解函数重载

发布时间:2026/7/26 6:25:44
C++深入浅出讲解函数重载 前言自然语言中一个词可以有多重含义人们可以通过上下文来判断该词真实的含义即该词被重载了。比如以前有一个笑话国有两个体育项目大家根本不用看也不用担心。一个是乒乓球一个是男足。前者是“谁也赢不了”后者是“谁也赢不了”函数重载1.1 函数重载的概念函数重载它是函数的一种特殊情况C允许在同一作用域中同一作用域中声明几个功能类似的同名函数函数重载的关键是函数的参数列表也称为“函数特征标”这些同名函数的形参列表(参数个数、类型和顺序(不同类型的顺序))必须不同常用来处理实现功能类似数据类型不同的问题函数重载也是多态的一种多态指的是“有多种形式”12345678910111213141516171819202122232425//C语言不支持重载C支持重载intAdd(intleft,intright){returnleftright;}doubleAdd(doubleleft,doubleright){returnleftright;}intAdd(intleft,doubleright){returnleftright;}intAdd(doubleleft,intright){returnleftright;}intmain(){Add(10, 20);Add(10.0, 20.0);Add(10, 20.0);Add(10.0, 20.0)return0;}下面两个函数属于函数重载吗1234567891011121314shortAdd(shortleft,shortright){returnleftright;}intAdd(shortleft,shortright){returnleftright;}intmain(){Add(10, 20);Add(10, 20);return0;}代码解析上述代码中的两个函数不属于函数重载因为重载的形参列表(参数个数、类型和顺序)必须不同函数重载与函数返回值的类型无关并且在函数调用时也是无法识别它的1.2 函数重载的意义意义在C语言中想要定义多个不同类型交换数据的子函数需要不同的函数名来命名比如SweapA、SweapB…等等1234567891011121314151617181920voidSweapA(int*pa,int*pb){inttemp *pa;*pa *pb;*pb temp;}voidSweapB(double*pa,double*pb){doubletemp *pa;*pa *pb;*pb temp;}intmain(){inta 10, b 20;doublec 10.0, d 20.0;SweapA(a, b);SweapB(c), d);return0;}但是在C中通过函数重载只需要命名一次就可以了虽然跟C语言一样要重复定义函数但是后面会学到函数模板后可以很好的解决这个重复定义问题1234567891011121314151617181920voidSweap(int*pa,int*pb){inttemp *pa;*pa *pb;*pb temp;}voidSweap(double*pa,double*pb){doubletemp *pa;*pa *pb;*pb temp;}intmain(){inta 10, b 20;doublec 10.0, d 20.0;Sweap(a, b);Sweap(c), d);return0;}1.3 名字修饰(name Mangling)名字修饰(name Mangling)C为了跟踪每一个重载函数它都会给这些函数指定一个私密身份使用C编译器编写函数重载程序时C编译器将执行一些奇特的操作 — — ---名称修饰 或 名称矫正它根据函数原型中指定的形参对每个函数名进行加密对参数数目和类型进行编码添加的一组符号符合随函数形参列表而异修饰时使用的约定函数名随编译器而异为什么C支持重载而C语言不支持呢在C/C中一个程序要运行起来需要经历以下几个阶段预处理、编译、汇编、链接预处理.i文件展开、宏替换、条件编译、去注释编译.s检查语法是否正确生成汇编代码汇编.o将汇编代码转化成二进制的机器码链接a.out生成符号表找调用函数的地址链接匹配合并到一起实际我们的项目通常是由多个头文件和多个源文件构成当前a.cpp中调用了b.cpp中定义的Add函数在编译后链接前的处理阶段a.o的目标文件中没有Add的函数地址因为Add是在b.cpp中定义的所以Add的地址在b.o中。那么怎么办呢链接器看到a.o调用Add但是没有Add的地址就会到b.o的符号表中找Add的地址然后链接到一起链接时面对Add函数链接器会使用哪个名字去找呢这里每个编译器都有自己的函数名修饰规则在Linux下使用gcc和g编译器演示函数名被修饰后的名字采用C语言编译器编译后结果反汇编结论在Linux下采用gcc编译完成后函数名字的修饰没有发生改变采用C编译器编译后结果反汇编结论在Linux下采用g编译完成后函数名字的修饰发生改变编译器将函数参数类型信息添加到修改后的名字中总结gcc的函数修饰后名字不变。而g的函数修饰后变成_Z函数长度函数名类型首字母C语言没办法支持重载因为同名函数没办法区分。而C是通过函数修饰规则来区分只要参数不同修饰出来的名字就不一样就支持了重载Windows下名字修饰规则结论对比Linux会发现windows下C编译器对函数名字修饰非常奇怪但道理都是一样的扩展学习C/C函数调用约定和名字修饰规则C函数重载C/C的调用约定接下来再演示一个例子f.h#include stdio.hvoid f(int a, double b);void f(double b, int a);f.cpp#include f.hvoid f(int a, double b);{printf(%d %lf\n, a, b)}void f(double b, int a);{printf(%lf %d\n, b, a)}Test.cpp#include f.hint main(){f(1, 2.222);f(2.222, 1);return 0;}编译后生成汇编指令链接时生成符号表Linux下gC编译器的命名Linux下gccC编译器的命名1.4 extern C有时候在C工程中可能需要将某些函数按照C的风格来编译但是大多数情况下是C工程需要将某些函数按照C的风格来编译C可以调用CPP的静态/动态库而CPP也可以调用C的静态/动态库extern “C”是告诉编译器它所声明的函数是C的库要用C的链接方式去调用静态库或动态库那么CPP是怎么调用C中的静态/动态库呢vs2022演示首先我们用C来生成一个静态库或动态库123456789101112131415161718192021222324252627Test.h#include stdio.hvoidPrintArray(int* p,intn);//显示数组内容voidInsertSort(int* p,intn);//插入排序Test.C#include Test.hvoidInsertSort(int* p,intn){for(inti 0; i n - 1; i){intend i;inttmp p[end 1];while(end 0){if(tmp p[end]){p[end 1] p[end];--end;}else{break;}}p[end 1] tmp;}}配置类型改成静态库后生成解决方案就得到后缀.lib文件了在CPP项目中添加新的库目录这个库是你生成的静态库的路径增加新的依赖项依赖项为生成静态库的文件名后缀Test.lib做完这些准备后我们来进行编译程序编译后我们发现链接阶段时出现了错误原因是C调用C时它们之间的函数命名规则名称修饰不同我们需要C中的extern C来解决extern “C”是告诉编译器它所声明的函数是C的库要用C的链接方式去调用静态库或动态库1234567891011121314151617181920externC{//../是在当前目录的上一个目录中找文件#include ../../Test/Test/Test.h}#include iostreamusingnamespacestd;voidTestInsertSort(){intArray[] { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };InsertSort(Array,sizeof(Array) /sizeof(Array[0]));for(inti 0; i 10; i)cout Array[i] ;cout endl;}intmain(){TestInsertSort();return0;}如果C想调用CPP的静态或动态库呢C调用CPP库时也会遇到名称修饰的问题这里需要对CPP的名称修饰规则改成C的规则这里我们需要用到条件编译来解决问题12345678910111213141516171819202122232425262728293031323334353637383940Test.h#include stdio.h#ifdef __cplusplus#define EXTERN_C extern C#else#define EXTERN_C#endifEXTERN_CvoidPrintArray(int* p,intn);EXTERN_CvoidInsertSort(int* p,intn);Test.cpp#include Test.hvoidPrintArray(int* p,intn){for(inti 0; i n; i){printf(%d , p[i]);}printf(\n);}voidInsertSort(int* p,intn){for(inti 0; i n - 1; i){intend i;inttmp p[end 1];while(end 0){if(tmp p[end]){p[end 1] p[end];--end;}else{break;}}p[end 1] tmp;}}感谢大家支持到此这篇关于C深入浅出讲解函数重载的文章就介绍到这了

相关新闻