C++函数重载(二)
有关重载匹配的问题,正常情况下,调用时候是根据形参和实参的匹配程度由编译器自动自动去选择一个最好的版本,若是通过函数指针去调用重载关系的函数时候,是根据定义函数指针时候的的类型来决定的应该调用哪一个匹配的重载函数,不再由实参类型决定了。正常情况下调用重载函数时候是由编译器根据实参的类型去匹配一个最好的版本,完全匹配就是最好的,但是在很多时候,可能有多个版本并不是完全匹配的,这种时候编译器是有一个优先级的,接下来总结函数匹配优先级的问题。
- 重载函数匹配的优先级:
(1)在调用重载关系的函数时,会根据实参和形参匹配程度,选择最优版本;
(2)当前g++编译器匹配的一般原则:
a:完全匹配//最高
b:常量转换//较好
c:升级转换//一般
d:降级转换//较差
e:省略号匹配//最差
重载匹配的优先级可以按照以上去记忆,关于C++标准中并没有明确特别详细的规定,一般都是由编译器决定的,大部分的编译器的匹配规则相同,因此可以按照以上规则优先级记忆。实际开发中,以实际开发场景即编译器为准,若不确定,可以按照以下代码进行测试,一般规则可以按照以上记忆,但它并不是语法的绝对的原则。不要将这个一般的规则当作语法的规定,它由编译器决定,不同的编译器可能会有所差异。实际开发中所用的的参数的差异一般较大,一般都是参数类型不相关的函数或者明显的个数上的差异,这种情况下一般不会出现这种模棱两可的场景,所以记忆一般按照以上的规则去记忆不会出问题。
//02overload.cpp
#include<iostream>
using namespace std;void bar(int i){cout<<"bar(1)"<<endl;
}void bar (const char c){cout<< "bar(2)"<<endl;
}/*以上两个函数:
相同作用域;
相同函数名;
不同参数。
因此以上两个函数是重载关系的函数。*/void foo(char c){cout<<"foo(1)"<<endl;
}
void foo(int i){cout<< "foo(2)"<<endl;
}/*以上两个foo()函数也构成有效的重载函数。*/ /*第三个版本的foo()形参是long long int,这样从short升级到long long int类型编译器会认为浪费了太多的内存,此时它的优先级会被认为和降级转换同一个优先级。若加上该版本的重载函数,那么在main的主函数中调用short s =100;foo(s);时候因为不能区分降级转换和这个过分的升级转换的优先级而报错。void foo(long long int i){cout<<"foo(3)"<<endl;
}
*//*省略号匹配,可以匹配
任意多个任意类型的实参,
最差匹配。//...表示不定参数,可以接受
任意多个任意类型的实参,例
如:printf()函数就有"..."的
参数,我们想打印的数据的个
数不确定,用“...”来接收。*/
void hum(int i,...){cout<< "hum(1)" <<endl;}void hum(int i,int j){cout<<"hum(2)"<<endl;
}
/*以上两个hum()函数也构成有效的重载函数。*/int main(void)
{/*调用函数时候,若参数为int型,则匹配第一个函数,若参数为const char 类型则匹配第二个函数。*/char c='A';/*两版本都能匹配上,如果说匹配第一个函数,那需要将实参char类型的转换为int,与第一个函数形参类型匹配之后,才能调用第一个函数。char转换为int,称为升级转换,因为char一个字节,int占用四个字节,单字节数据到多字节数据称为升级转换。 如果匹配第二个函数,给实参char附加了一个常数属性,char转换为const char,这个转换叫常量转换。这两个版本的bar()函数都不是完全匹配,若是第一个函数的话,从char到int的升级转换,由单个字节到4个字节的转换是有内存浪费的。若是第二个函数的话,仅仅是附加了一个const的常属性。对于这两种情况同时存在的时候,认为常量转换更好点。在编译器的理解上,常量转换和完全匹配几乎就一样,相比于升级转换浪费内存来说更优。所以应该调用的是第二个版本的函数bar()。*/bar(c);//bar(2)/*若实参类型为char 类型,那么就会调用第一个foo()函数,若为int 类型则会调用第二个foo()函数。现在既不传参int类型,也不传参char类型,传实参类型为short类型,进行研究。*/short s =100;/*将short类型的实参传递过去,根据语法规则,单字节整数char,4字节整数int,以及实参的short类型都是可以相互转换的,现在看哪一个匹配更好。若与第一个匹配,需要将short类型转换为char类型,short类型为两个字节,char为一个字节,将这种多字节到单字节的转换称为:降级转换。若与第二个foo()函数匹配,那需要将short类型转换为int类型,将2个字节的转换为4个字节的从字节少的到字节多的浪费内存的转换称为:升级转换。当这两种情况同时存在时候,需要选择一个更好的,可以从内存角度进行分析。升级转换不如常量转换,需要浪费内存,但是,它能够保证数据的完整性;而降级转换,不浪费内存,但是,需要损失值域,一个char类型的变量能够表示的范围是2^8,一个short类型的变量能够表示的范围是2^16,很明显,从short到char转换会丢掉一个字节的数据,这个值域损失比较严重。若现在的short类型的s是一个大于127的数据,那降级转换会使得接收时候数据有所损失,导致程序的一个错误结果,这中降级转换,损失值域,有一定的风险。两种情况相比较,升级转换更好点。因此着各种情况下会选择第二个版本的foo()函数。*/foo(s);//foo(2)/*第一个实参10匹配形参int类型,第二个实参1.23匹配双精度的浮点类型double类型。double为8个字节,int为4个字节,从多字节数据类型转换为短字节类型的数据称为:降级转换。一般印象中double到int的降级转换应该是很差劲,但编译器的设计者不这么想,"..."的匹配是不确定的事件,在不确定的事件可以看作是不靠谱的事件,例如:中午吃饭时候,几个同学商量说去吃饭,某位同学提议吃盒饭,另外一位同学提议吃包子,有人反对说盒饭不好吃,包子是早餐,该同学被反问道:“那吃啥好呢?”,回到道:“吃啥都行”,这里的"吃啥都行"感觉好像是“吃啥都不行”,因为它总是能挑出点毛病出来,像这种不确定的事表达的就是不靠谱不准确的,因此在编译器设计者看来"..."表达的是这个不靠谱不准确的意思,“...”确实是什么都可以匹配,但是是不靠谱的匹配,有确定的匹配的话是不会选择"..."类型的匹配的。因此将“...”看作是最差的匹配,比降级转换还要差的匹配方式,因为转换是一个确定的匹配,而"..."是不确定的匹配。因此会选择第二个hum()函数进行匹配。*/hum(10,1.23);//hum(2)
}函数重载匹配的一般原则:完全匹配的最好,
其次是升级转换,其次为降级转换,
最次是省略号匹配。【常量转换和升级转换差不多,
比升级转换稍微好点】
