首页 > 编程知识 正文

什么叫重载函数,函数重载的作用是什么

时间:2023-05-03 06:24:58 阅读:228309 作者:518

为什么会存在函数重载?
有时候需要根据不同的数据类型调用不同名的函数,如果这种情况比较多的话,对于写程序的人来说,要分别编写功能相同而名字不同的函数,是很不方便的。所以在c++中引入了函数重载。
那什么又是函数重载呢?
函数重载是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数、类型、顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。函数的重载即对一个函数名重新赋予它新的含义,使一个函数名可以多用。**所谓重载,其实就是“一物多用”。

以下是关于函数重载的例子:

void Test(){}void Test(int a){}//这两个函数形成重载,因为形参的个数不同int Add(int left,char right){}int Add(int left, int right){}//此两个函数形成重载,因为形参类型不同void Test(int a,char b){}void Test(char a,int b){}//此两个函数形成重载,因为形参类型的次序不同 void Test(){}int Test(){}//这两个函数并不能形成重载,因为函数重载是与返回值类型无关的。 int Add(int left, int right) { return left+right; } double Add(double left, double right) { return left+right; } long Add(long left, long rigjt) { return left+right;} int main(){ //系统会根据参数的不同而找到对应的函数并调用。 Add(10, 20); Add(10.0, 20.0); Add(10L, 20L); return 0; }

c语言中不支持函数重载,在c++中却可以支持函数重载,这是为什么呢?
翻译过程一般分为: 预处理—-编译——汇编——链接
预处理:展开头文件,宏替换,去掉注释,条件编译等工作。生成.i文件
编译:语法检查,生成汇编代码。 生成.s文件
汇编:将汇编代码转成机器码 。 生成.o文件
链接:将之前生成的文件链接到一起,生成可执行文件。 生成.out文件
如果有多个.c文件,注意 在链接之前各文件都是独立向下进行的,各文件之间没有交集。
所以支持重载问题就出在链接这个阶段上,c语言在链接的时候根据函数名找要调用的函数,而c++而是根据函数名和参数类型来寻找要调用的函数(函数名修饰规则)
在c语言和c++中对函数名字的处理规则不一样。编译器在对函数编译完成后,实际上已经对函数的名字进行修改了。如下这两个函数形成重载了
int Add(int left,char right)
int Add(int left, int right)
在c语言中,编译器把这两个函数的名字都修改成了_Add,这两个函数无法区分,所以c语言中无法支持函数重载。但在c++中两者的函数名分别被修改成了
(?Add@@YAHHD@z)
(?Add@@YAHHH@z)
所以c++可以进行函数重载。
C++中函数重载底层是怎么处理的?
大家可以参考一下这两篇博客:
http://www.cnblogs.com/skynet/archive/2010/09/05/1818636.html http://blog.csdn.net/lioncolumn/article/details/10376891
C++中能否将一个函数按照C的风格来编译?
C++中可以通过在函数声明前加 extern “C” 将一个函数按照 C 语言的风格来进行编译。函数在C中和C++中编译过的函数名字是不一样的。加上extern”C”是说明是说明C已经编译过的。 C++想要调用已经编译过的C函数,由于编译过的名字不同,是不能直接调用的,所以C++加extern“C”声明来解决这个问题。
例如:假设某个函数的原型为:int add(int left, int right);该函数被C 编译器编译后在库中的名字为_add, 而C++ 编译器则会产生像_add_int_int 之类的名字,加上extren”C”后,就相当于告诉编译器,函数add是个C编译后的函数,在库里应该找的是_add,而不是_add_int_int.
需要注意的是:不同的编译器有不同的重命名方式,这里仅仅举例说明,实际情况可能并非如此。
下面两个函数能形成函数重载吗?

void FunTest(int a = 10){ cout<<"void FunTest(int)"<<endl;} void FunTest(int a){ cout<<"void FunTest(int)"<<endl; }

第一个函数在调用时,可以传参,也可以不传参,不传参就用给出的这个缺省值。第二个函数在调用时一定要传参。所以,当传参时,这两个函数无法确认调用哪一个。

引用
在c语言中函数传参有2种方式,即传值和传址:
1.传值

void Swap(int left, int right) { int temp = left; left = right; right = temp; }传值:在函数调用过程中会生成一份临时变量,最终把实参的值传递给新分配的临时变量即形参 优点:避免了函数调用的副作用 缺点:无法改变形参的值如果想通过形参改变实参的值,只能通过指针传递

2.传址

void Swap(int* pLeft, int* pRight) { int temp = *pLeft; *pLeft = *pRight; *pRight= temp; }

指针可以解决问题,但不是很形象友好,不安全 ,是否有一种类型:可以向值一样传递,同时又能达到指针的效果呢?所以在c++里我们引入了引用。
那什么又叫引用呢?
从应用层方面来理解引用:
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间 。从底层来理解引用的话,这段话是有问题的,后面我会来讲。
类型& 引用变量名(对象名) = 引用实体;
注意:引用变量必须和引用实体是同种类型的 。
引用特性
引用在定义时必须初始化

一个变量可以有多个引用

引用一旦引用一个实体,再不能引用其他实体

int a=10;int b=20;int &ra=a;int &ra=b;//ra引用了a后不可以再引用b

常引用

void TestConstRef() { const int a = 10; //int& ra = a; // 该语句编译时会出错, a 为常量 可改为 const int& ra = a; // int& b = 10; // 该语句编译时会出错, b 为常量 可改为 const int& b = 10; int c=10;//c可读可写 const int& rc=c;//c的值是什么就是什么,rc只能读无权修改 double d = 12.34; //int& rd = d; // 该语句编译时会出错,类型不同 可改为 const int& rd = d; }

上述最后一个为什么加const就可以了呢?
让int类型的rd去引用double类型的d,是无法引用的。底层是怎么处理的呢?实际上找了一个中间变量过渡,把d的整数部分12复制到临时变量中,然后让rd引用这个临时变量,所以d不是rd所引用的实体,中间变量才是。为什么要加const呢?因为存放临时变量的空间不知道名字,不知道地址,所以访问不了,既然访问不了,这块空间就有一个常属性,所以要用常引用。
数组也是可以引用的,如下:

int array[10];int (&rarray)[10]=array;//int [10]是数组的类型

引用的使用场景
作为函数形参
作为函数返回值


int& TestRefReturn(int& a)
{
a += 10;
return a;
}

我们看一下下图这段程序存在什么问题:

比较奇怪的一点是,第二次第三次打印出来的是随机值,这是为什么呢?
代码从主函数的位置跳到Add函数的位置,Add函数要执行系统给分配的栈函数,Add函数返回一个引用类型的变量,ret以引用的形式来接收Add函数的返回值,ret实际上是a的别名,因此打印的地址是一样的,Add栈里有一个a的空间,Add函数返回会把对应的空间归还给系统,归还时没有把栈空间遗留的垃圾带走,_cdecl有一个说法是下一次哪一个函数调用到这块空间了,由主调函数自己去清理。所以第一次得到30,第二三次得到清理过后的随机值。

引用和指针的区别
相同点
底层的实现方式相同,都是按照指针的方式来实现。


从底层来看,底层已经把引用变量当成指针了,指针变量会保存它所引用的实体的地址,当然这个指针它肯定是需要空间来保存的,因此在底层的处理方式这个引用变量实际上编译器也给它分配了空间,空间里面保存了它所引用实体的地址。

不同点
引用在定义时必须初始化,指针没有要求
一旦一个引用被初始化为指向一个对象,就不能再指向其他对 象,而指针可以在任何时候指向任何一个同类型对象
没有NULL引用,但有NULL指针
在sizeof中含义不同:引用结果为引用类型的大小,但指针始 终是地址*空间所占字节个数
引用自加改变变量的内容,指针自加改变了指针指向
有多级指针,但是没有多级引用
指针需要手动寻址,引用通过编译器实现寻址
引用比指针使用起来相对更安全

这段代码在执行到left=right就异常终止了,这是为什么呢?
如果我们真的从底层理解了引用的话,那这个问题应该很容易就想明白了。
之所以在left=right出错,是因为引用在底层当成指针来处理,对NULL解引用,所以程序崩溃。

版权声明:本文著作权归原作者所有,欢迎分享本文,谢谢支持!
转载请注明:函数重载和引用 作者:jsddt1_

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。