本资料由-大学生创业|创业|创业网提供资料在线代理|网页代理|代理网页|减肥药排行榜|淘宝最好的减肥药|什么减肥药效果最好|减肥瘦身药|——每个现象后面都隐藏一个本质,关键在于我们是否去挖掘写在前面:函数重载的重要性不言而明,但是你知道C++中函数重载是如何实现的呢(虽然本文谈的是C++中函数重载的实现,但我想其它语言也是类似的)?这个可以分解为下面两个问题1、声明/定义重载函数时,是如何解决命名冲突的?(抛开函数重载不谈,using就是一种解决命名冲突的方法,解决命名冲突还有很多其它的方法,这里就不论述了)2、当我们调用一个重载的函数时,又是如何去解析的?(即怎么知道调用的是哪个函数呢)这两个问题是任何支持函数重载的语言都必须要解决的问题!带着这两个问题,我们开始本文的探讨。本文的主要内容如下:1、例子引入(现象)o什么是函数重载(what)?o为什么需要函数重载(why)?2、编译器如何解决命名冲突的?o函数重载为什么不考虑返回值类型3、重载函数的调用匹配o模凌两可的情况4、编译器是如何解析重载函数调用的?o根据函数名确定候选函数集o确定可用函数o确定最佳匹配函数5、总结1、例子引入(现象)1.1、什么是函数重载(what)?函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。Whentwoormoredifferentdeclarationsarespecifiedforasinglenameinthesamescope,thatnameissaidtooverloaded.Byextension,twodeclarationsinthesamescopethatdeclarethesamenamebutwithdifferenttypesarecalledoverloadeddeclarations.Onlyfunctiondeclarationscanbeoverloaded;objectandtypedeclarationscannotbeoverloaded.——摘自《ANSIC++Standard.P290》本资料由-大学生创业|创业|创业网提供资料在线代理|网页代理|代理网页|减肥药排行榜|淘宝最好的减肥药|什么减肥药效果最好|减肥瘦身药|看下面的一个例子,来体会一下:实现一个打印函数,既可以打印int型、也可以打印字符串型。在C++中,我们可以这样做:1.#includeiostream2.usingnamespacestd;3.4.voidprint(inti)5.{6.coutprintainteger:iendl;7.}8.9.voidprint(stringstr)10.{11.coutprintastring:strendl;12.}13.14.intmain()15.{16.print(12);17.print(helloworld!);18.return0;19.}通过上面代码的实现,可以根据具体的print()的参数去调用print(int)还是print(string)。上面print(12)会去调用print(int),print(helloworld)会去调用print(string),如下面的结果:(先用g++test.c编译,然后执行)1.2、为什么需要函数重载(why)?试想如果没有函数重载机制,如在C中,你必须要这样去做:为这个print函数取不同的名字,如print_int、print_string。这里还只是两个的情况,如果是很多个的话,就需要为实现同一个功能的函数取很多个名字,如加入打印long型、char*、各种类型的数组等等。这样做很不友好!类的构造函数跟类名相同,也就是说:构造函数都同名。如果没有函数重载机制,要想实例化不同的对象,那是相当的麻烦!操作符重载,本质上就是函数重载,它大大丰富了已有操作符的含义,方便使用,如+可用于连接字符串等!本资料由-大学生创业|创业|创业网提供资料在线代理|网页代理|代理网页|减肥药排行榜|淘宝最好的减肥药|什么减肥药效果最好|减肥瘦身药|通过上面的介绍我们对函数重载,应该唤醒了我们对函数重载的大概记忆。下面我们就来分析,C++是如何实现函数重载机制的。2、编译器如何解决命名冲突的?为了了解编译器是如何处理这些重载函数的,我们反编译下上面我们生成的执行文件,看下汇编代码(全文都是在Linux下面做的实验,Windows类似,你也可以参考《一道简单的题目引发的思考》一文,那里既用到Linux下面的反汇编和Windows下面的反汇编,并注明了Linux和Windows汇编语言的区别)。我们执行命令objdump-da.outlog.txt反汇编并将结果重定向到log.txt文件中,然后分析log.txt文件。发现函数voidprint(inti)编译之后为:(注意它的函数签名变为——_Z5printi)发现函数voidprint(stringstr)编译之后为:(注意它的函数签名变为——_Z5printSs)本资料由-大学生创业|创业|创业网提供资料在线代理|网页代理|代理网页|减肥药排行榜|淘宝最好的减肥药|什么减肥药效果最好|减肥瘦身药|我们可以发现编译之后,重载函数的名字变了不再都是print!这样不存在命名冲突的问题了,但又有新的问题了——变名机制是怎样的,即如何将一个重载函数的签名映射到一个新的标识?我的第一反应是:函数名+参数列表,因为函数重载取决于参数的类型、个数,而跟返回类型无关。但看下面的映射关系:voidprint(inti)-->_Z5printivoidprint(stringstr)-->_Z5printSs进一步猜想,前面的Z5表示返回值类型,print函数名,i表示整型int,Ss表示字符串string,即映射为返回类型+函数名+参数列表。最后在main函数中就是通过_Z5printi、_Z5printSs来调用对应的函数的:80489bc:e873ffffffcall8048934_Z5printi……………80489f0:e87affffffcall804896f_Z5printSs我们再写几个重载函数来验证一下猜想,如:voidprint(longl)-->_Z5printlvoidprint(charstr)-->_Z5printc可以发现大概是int->i,long->l,char->c,string->Ss….基本上都是用首字母代表,现在我们来现在一个函数的返回值类型是否真的对函数变名有影响,如:1.#includeiostream本资料由-大学生创业|创业|创业网提供资料在线代理|网页代理|代理网页|减肥药排行榜|淘宝最好的减肥药|什么减肥药效果最好|减肥瘦身药|(inta,intb)5.{6.returna=b?a:b;7.}8.9.doublemax(doublea,doubleb)10.{11.returna=b?a:b;12.}13.intmain()14.{15.coutmaxintis:max(1,3)endl;16.coutmaxdoubleis:max(1.2,1.3)endl;17.return0;18.}intmax(inta,intb)映射为_Z3maxii、doublemax(doublea,doubleb)映射为_Z3maxdd,这证实了我的猜想,Z后面的数字代码各种返回类型。更加详细的对应关系,如那个数字对应那个返回类型,哪个字符代表哪重参数类型,就不去具体研究了,因为这个东西跟编译器有关,上面的研究都是基于g++编译器,如果用的是vs编译器的话,对应关系跟这个肯定不一样。但是规则是一样的:“返回类型+函数名+参数列表”。既然返回类型也考虑到映射机制中,这样不同的返回类型映射之后的函数名肯定不一样了,但为什么不将函数返回类型考虑到函数重载中呢?——这是为了保持解析操作符或函数调用时,独立于上下文(不依赖于上下文),看下面的例子1.floatsqrt(float);2.doublesqrt(double);3.4.voidf(doubleda,floatfla)5.{6.floatfl=sqrt(da);//调用sqrt(double)7.doubled=sqrt(da);//调用sqrt(double)8.9.fl=sqrt(fla);//调用sqrt(float)10.d=sqrt(fla);//调用sqrt(float)11.}如果返回类型考虑到函数重载中,这样将不可能再独立于上下文决定调用哪个函数。本资料由-大学生创业|创业|创业网提供资料在线代理|网页代理|代理网页|减肥药排行榜|淘宝最好的减肥药|什么减肥药效果最好|减肥瘦身药|至此似乎已经完全分析清楚了,但我们还漏了函数重载的重要限定——作用域。上面我们介绍的函数重载都是全局函数,下面我们来看一下一个类中的函数重载,用类的对象调用print函数,并根据实参调用不同的函数:1.#includeiostream2.usingnamespacestd;3.4.classtest{5.public:6.voidprint(inti)7.{8.coutintendl;9.}10.voidprint(charc)11.{12.coutcharendl;13.}14.};15.intmain()16.{17.testt;18.t.print(1);19.t.print('a');20.return0;21.}我们现在再来看一下这时print函数映射之后的函数名:voidprint(inti)--_ZN4test5printEivoidprint(charc)-->_ZN4test5printEc注意前面的N4test,我们可以很容易猜到应该表示作用域,N4可能为命名空间、test类名等等。这说明最准确的映射机制为:作用域+返回类型+函数名+参数列表3、重载函数的调用匹配现在已经解决了重载函数命名冲突的问题,在定义完重载函数之后,用函数名调用的时候是如何去解析的?为了估计哪个重载函数最适合,需要依次按照下列规则来判断:精确匹配:参数匹配而不做转换,或者只是做微不足道的转换,如数组名到指针、函数名到指向函数的指针、T到constT;本资料由-大学生创业|创业|创业网提供资料在线代理|网页代理|代理网页|减肥药排行榜|淘宝最好的减肥药|什么减肥药效果最好|减肥瘦身药|提升匹配:即整数提升(如bool到int、char到int、short到i