C++中函数重载与引用的操作方法

C++中函数重载与引用的操作方法
1. 函数重载在中文语境中有些词语它就是一词多义的人们通过上下文来判断词语的意思即该词被重载了。就比如说以前有个笑话我们国家有两个体育项目大家根本不用看也不用担心。一个是乒乓球一个是足球。前者谁也赢不了后者谁也赢不了。1.1 函数重载概念函数重载是函数的一种特殊情况C允许在同一作用域中声明几个功能类似的同名函数这些函数的形参列表参数个数或者类型或者类型顺序不同常用来处理功能类似数据类型不同的问题。1234567891011121314151617181920212223242526272829303132333435363738394041#include iostreamusingnamespacestd;//1.参数类型不同intadd(intleft,intright){coutint add(int left,int right)endl;returnleftright;}doubleadd(doubleleft,doubleright){coutdouble add(double left,double right)endl;returnleftright;}//2.参数个数不同voidtest(){couttest()endl;}voidtest(inta){couttest(int a)endl;}//3.参数类型顺序不同voidtest2(inta,charb){couttest2(int a,char b)endl;}voidtest2(charb,inta){couttest2(char b,int a)endl;}intmain(){add(1,2);add(1.1,2.2);test();test(100);test2(10,a);test2(a,10);return0;}1.2 C支持函数重载的原理–名字修饰name Mangling为什么C支持函数重载而C语言不支持函数重载呢在C/C中一个程序要运行起来需要经历以下几个阶段预处理、编译、汇编、链接。实际项目中通常是由多个头文件和多个源文件构成的而通过C语言阶段学习的编译链接我们可以知道【当前a.cpp中调用了b.cpp中定义的Add函数时】编译后链接前a.o的目标文件中没有Add的函数地址因为Add是在b.cpp中定义的所以Add的地址在b.o中。那么怎么办呢所以链接阶段就是专门处理这种问题的链接器看到a.o调用Add但是没有Add的地址就会到b.o的符号表中找Add的地址然后链接到一起。那么链接时面对Add函数链接器会使用哪个名字去找呢这里每个编译器都有自己的函数名修饰规则。由于windows下vs的修饰规则过于复杂而Linux下g的修饰规则简单易懂下面我们使用了g演示这个修饰后的名字。通过下面我们可以看到gcc的函数修饰后名字不变。而g的函数修饰后变成【__Z函数长度函数名类型首字母】采用C语言编译器编译后的结果12345678910111213141516//file.c中的代码#include stdio.hintAdd(inta,intb){returnab;}voidtest(inta,doubleb,int*p){//...}intmain(){Add(10,20);test(1,2,0);return0;}结论在Linux下采用gcc编译完成后函数名字的修饰没有发生改变。采用C编译器编译后结果结论在Linux下采用g编译完成后函数名字的修饰发生改变编译器将函数参数类型信息添加到修改后的名字中。windows下名字修饰规则总结对比Linux会发现Windows下vs编译器对函数名字修饰规则相对复杂难懂但道理类似。6. 通过这里就理解了C语言没办法支持重载因为同名函数没办法区分。而C是通过函数修饰规则来区分地只要参数不同修饰出来地名字就不一样那么就可以支持重载了。7. 如果两个函数函数名和参数是一样的返回值不同是不构成重载的因为调用时编译器没办法区分。2. 引用2.1 引用的概念引用不是新定义一个变量而是给已存在变量取一个别名编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间。就像外号一样尽管名字不同但人都是一个人。语法类型 引用变量名(对象名) 引用实体123456789101112131415161718#include cstdiovoidtest(){inta 10;int ra a;//定义引用类型printf(%p\n, a);printf(%p\n, ra);}intmain(){test();return0;}//打印结果:/*009EFDE0009EFDE0*/注意引用类型必须和引用实体是同种类型的。2.2 引用的特性引用在定义时必须初始化。一个变量可以有多个引用。引用一旦引用一个实体就不能再引用其他实体。12345678910111213141516voidtest(){inta 10;//int ra;//引用在定义时必须初始化,否则报错int ra a;int rra a;printf(%p\n, a);printf(%p\n, ra);printf(%p\n, rra);}//打印结果:/*00AFF9A000AFF9A000AFF9A0*/2.3 常引用1234567891011voidtestconstref(){constinta 10;//int ra a;//该语句编译时会出错a为常量constint ra a;//int b 10;//该语句编译时会出错10为常量constint b 10;doubled 3.14;//int rd d;该语句编译时会出错类型不同constint rd d;}2.4 使用场景做参数123456voidSwap(int left,int right){inttemp left;left right;right temp;}做返回值1234567int Count(){staticintn 0;n;//...returnn;}观察下来代码会输出什么结果123456789101112131415161718#include iostreamusingnamespacestd;int add(inta,intb){intc a b;returnc;}intmain(){int ret add(2, 3);add(4, 5);cout add(2,3) is : ret endl;return0;}//打印结果/*add(2,3) is :9*/函数运行时系统需要给函数开辟独立的栈空间用来保存函数的形参局部变量以及一些寄存器信息等。函数运行结束后该函数对应的栈空间就被系统回收了。空间被回收指该栈空间暂时不能使用但是内存还在比如上课申请教室上完课之后教师归还给学校但是教室本身还在不能说归还后教室就消失了。注意如果函数返回了出了函数作用域如果返回对象还在没有还给系统则可以使用引用返回如果已经还给系统了则必须使用传值返回。2.5 传值、传引用效率比较以值作为参数或者返回类型在传参和返回期间函数不会直接传递实参或者将变量本身直接返回而是传递实参或者返回变量的一份临时拷贝因此用值作为参数或者返回类型效率是非常低下的尤其是当参数或返回类型非常大时效率就更低。演示效率对比值和引用作为参数类型的性能对比1234567891011121314151617181920212223242526272829303132333435363738394041#include iostream#include ctimeusingnamespacestd;structA{inta[10000];};voidTestFunc(A a){}voidTestFunc2(A a){}intmain(){A a;//以值作为函数参数size_tbegin1 clock();for(size_ti 0; i 10000; i){TestFunc(a);}size_tend1 clock();//以引用作为函数参数size_tbegin2 clock();for(size_ti 0; i 10000; i){TestFunc2(a);}size_tend2 clock();//分别计算两个函数运行结束后的时间cout Testfunc(A) time: end1 - begin1 endl;cout Testfunc2(A) time: end2 - begin2 endl;return0;}//打印结果/*Testfunc(A) time:12Testfunc2(A) time:0(单位毫秒)*/演示值和引用的作为返回类型的性能对比1234567891011121314151617181920212223242526272829303132333435363738394041424344#include iostream#include ctimeusingnamespacestd;structA{inta[10000];};structA a;//值返回A TestFunc(){returna;}//引用返回A TestFunc2(){returna;}intmain(){//以值作为函数的返回类型size_tbegin1 clock();for(size_ti 0; i 10000; i){TestFunc();}size_tend1 clock();//以引用作为函数的返回类型size_tbegin2 clock();for(size_ti 0; i 10000; i){TestFunc2();}size_tend2 clock();//分别计算两个函数运行结束后的时间cout Testfunc(A) time: end1 - begin1 endl;cout Testfunc2(A) time: end2 - begin2 endl;return0;}//打印结果/*Testfunc(A) time:25Testfunc2(A) time:1*/结论通过上述的代码可以清楚的发现传值和传引用在作为传参以及返回值类型上效率相差很大。2.6 引用和指针的区别在语法层面上呢引用就是一个别名没有独立空间和其被引用体共用一块空间。123456789101112131415#include iostreamusingnamespacestd;intmain(){intval 100;int rval val;coutval valendl;coutrval rvalendl;return0;}//打印结果/*val 003AFB98rval 003AFB98*/但是呢在底层方面实际是由空间的因为引用是按照指针方式来实现的。1234567891011#include iostreamusingnamespacestd;intmain(){inta 10;int ra a;ra 20;int* pa a;*pa 20return0;}反汇编可以看到操作是类似的。引用和指针的不同点引用概念上定义一个变量的别名指针存储一个变量地址。引用在定义时必须初始化指针就没有要求。引用在初始化时引用一个实体后就不能再引用其他实体而指针可以再任何时候指向任何一个同类型实体。没有NULL引用但是又NULL指针再sizeof中含义不同引用结果为引用类型的大小但是指针始终是地址空间所占字节数个根据所在平台确定如32位平台占4个字节引用自加即引用的实体加1指针自加即指针向后偏移一个类型的大小。由多级指针但是没有多级引用。访问实体方式不同指针需要显示解引用引用编译器自己处理。

最新新闻

日新闻

周新闻

月新闻