C++中函数重载的原理

来源:互联网 发布:淘宝积分在哪里看到 编辑:程序博客网 时间:2024/06/05 18:46

函数重载的定义

函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表(参数个数、参数类型、参数顺序)的函数,这组函数被称为重载函数。重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。

C语言函数不能重载的原因?
C语言使用的编译器是gcc(.c),C++使用的编译器是g++(.cpp),C语言通过编译器生成的函数符号由函数名决定,而C++通过编译器生成的函数符号由函数名和参数共同决定。
扩展:静多态和动多态。
静多态:编译链接阶段,动多态:运行阶段;
静多态:重载、模板,动多态:继承和虚函数。
问题:

//不能构成重载,因为const不参与类型void func1(int a){}void func1(const int a){}//不能构成重载,跟const的作用一样,不参与类型void func2(int a){}void func2(volatile int a){}//能构成重载,const右边有*号void func3(int *a){}void func3(const int *a){}//能构成重载,volatile右边有*号void func4(int *a){}void func4(volatile int *a){}

总结:引用和指针所指对象的常量性不同可以重载,而非引用类型的参数本身的常量性不同不能重载。
重载的重要条件:看能不能通过修改形参来影响实参的值。

函数重载的原则

①函数名相同,函数参数列表不同(类型、个数、顺序)
②匹配原则1:严格匹配,找到再调用
③匹配原则2:通过隐式类型转换寻求一个匹配,找到则调用
④返回值类型不构成重载条件

重载的使用

例1:

/*要是放在C语言中,这段代码必然是编译不通过,而在C++中是合乎重载原则的*/#include <iostream>using namespace std;float abs(float i){    return (i >= 0 ? i : -i);}double abs(double i){    return (i >= 0 ? i : -i);}int abs(int i){    return (i >= 0 ? i : -i);}int main(){    /*abbiguous二义性*/    float i = abs(-5.5f);/*默认调用第一个abs*/    float j = abs(-4.4);/*默认调用第二个,如果注销掉第二个abs,编译时出错报二义性错误*/    cout << i << endl;    cout << j << endl;    /*浮点数默认大小(类型)*/    cout << "sizeof(4.5)=" << sizeof(4.5) << endl;/*default*/  //8    cout << "sizeof(4.5f)=" << sizeof(4.5f) << endl;//4    return 0;}

注意:
重载时的二义性:如果计算机存在有两种隐式转换选择,计算机不会去选,而报二义性错误
eg1:double可以隐式转换为float或int,如果abs(-4.4)并且定义float与int分别作为参数的ads(),编译时则会产生二义性
eg2:double->int/float会产生两义性,int->long/double也会产生两义性。
即两个特例重载时需要注意。 为了避免重载中的二义性问题,使用时按所需强制转换,不要让计算机去自己选择。

重载的原理

1、重载原理:name mangling(命名倾轧)
对于下面这段程序来说:

#include <iostream>using namespace std;//不设置,C++编译器默认倾轧void func(int a){cout<<"a = "<<a<<endl;}void func(char a){cout<<"a = "<<a<<endl;}void func(int a, char b){cout<<"a = "<<a<<endl<<"b = "<<b<<endl;}void func(char a, int b){cout<<"a = "<<a<<endl<<"b = "<<b<<endl;}int main(void){    int a = 10;    char b = 'b';    func(a);    func(b);    func(a,b);    func(b,a);    return 0;}

上面的程序在经过C++编译器编译时就类似于变成了下面这种写法,这种写法与其重载函数名以及参数类型有关:

#include <iostream>using namespace std;/*定义自动倾轧*/void func_i(int a){cout<<"a = "<<a<<endl;}void func_c(char a){cout<<"a = "<<a<<endl;}void func_ic(int a, char b){cout<<"a = "<<a<<endl<<"b = "<<b<<endl;}void func_ci(char a, int b){cout<<"a = "<<a<<endl<<"b = "<<b<<endl;}int main(void){    int a = 10;    char b = 'b';    /*调用也自动倾轧*/    func_i(a);    func_c(b);    func_ic(a,b);    func_ci(b,a);    return 0;}

2、C++在兼容C时的不倾轧操作
在C++中,定义阶段与使用操作阶段均会进行倾轧(编译时倾轧),使用extern “C”,可以将某函数设置为不倾轧,可C++需要倾轧以支持重载,为什么弄一个不倾轧出来呢?
分析:
首先,函数定义阶段与使用要么都倾轧,要么都不倾轧,必须得一一对应,否则会报函数找不到的错误。倾轧是编译时进行的,而对于C++要兼容C的问题,C++就必须兼容C的语法与C库(链接库),C库只在连链接时加入,不存在让C++编译器去倾轧的问题;而C头文件中声明的C库函数在调用时会倾轧,要想使用不参加倾轧的C库函数,C++中编译时就不能倾轧C的头文件中对于库函数的声明,即C库中已经不能修改不倾轧为倾轧,则必须将头文件中的声明也设置为不倾轧,以此与库中相互对应。如果查看C的标准头文件,可以发现其中有一个extern “C”,表示不倾轧调用时的函数名。

如下是string.h头文件中的一部分:

/*查看string.h,发现在函数声明之前,就对C++编译器的编译方式进行声明extern "C",即声明为:C++编译器在编译时遇到C语言库对应的调用的函数名不倾轧*/#ifndef _INC_STRING#define _INC_STRING#ifdef __cplusplus  //如果是C++编译器就要进行不倾轧设置extern "C" {#endif... //函数声明等#ifdef __cplusplus//与上面匹配}#endif#endif  /* _INC_STRING */

举例说明:

#include <iostream>#include <string.h>using namespace std;extern "C"{    void func(int a){cout<<"a = "<<a<<endl;}    //error C2733: 不允许重载函数“func”的第二个 C 链接    void func(int a,double b){cout<<"a = "<<a<<endl;}}int main(void){    int a = 10;    func(a);    return 0;}

这样的代码是不能够完成编译的(因为不倾轧就会使得两个函数名一样,产生重名,而重载也是不重名的)。而要上面的函数能够正常调用,定义与调用时均遵循默认的倾轧(而且必须是倾轧,否则会产生二义性),做法如下:

#include <iostream>#include <string.h>using namespace std;//倾轧就不会重名void func(int a){cout<<"a = "<<a<<endl;}void func(int a,double b){cout<<"a = "<<a<<endl;}int main(void){    int a = 10;    func(a);    return 0;}

再用一个例子测试:

/*func.h*/#ifndef FUNC_H_#define FUNC_H_extern "C" void func(int);//声明为不倾轧#endif
/*func.c*/#include<iostream>using namespace std;//定义不倾轧extern "C"{    void func(int a){cout<<"a = "<<a<<endl;}}
/*main.cpp*/#include<iostream>#include"func.h"using namespace std;int main(void){    int a = 10;    func(a);    return 0;}

测试结果如下:
声明中不加extern “C”,但定义中加extern “C”,编译出错:
name mangling
总之一句话,倾轧是C++为了实现函数重载而设计的,不倾轧的extern “C”则是为了兼容C而后实现的。我们编程一般犯不着对自定义的C++函数设置。

参考:http://blog.csdn.net/apollon_krj/article/details/60760586

继续学习:http://www.cnblogs.com/skynet/archive/2010/09/05/1818636.html
http://blog.csdn.net/qq_35280514/article/details/67675034
http://blog.csdn.net/shltsh/article/details/45938663
http://blog.csdn.net/llzk_/article/details/51763566
http://blog.csdn.net/zhanghow/article/details/53588458
http://blog.csdn.net/u011088390/article/details/24724985

原创粉丝点击