C++函数重载与重载原理:命名倾轧

来源:互联网 发布:淘宝详情页ps怎么做 编辑:程序博客网 时间:2024/06/05 17:04

一、重载概念与原则:

1、重载概念:

在C语言中,一个函数不能与另一个函数重名,而在C++中,只要一个函数的参数列表与另一个函数的参数列表不完全相同,函数名就可以相同。C++这一特点就是所谓函数的重载现象。

同一个名字因为参数列表不同,展现了不同的结果,也叫静多态。

2、重载原则:

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

/*要是放在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*/    cout << "sizeof(4.5f)=" << sizeof(4.5f) << endl;    return 0;}

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

二、重载原理:命名倾轧(name mangling)

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”,表示不倾轧C的函数声明。如下是string.h头文件中的一部分:

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

分别查看不同集成承环境中的string.h文件,都是有对函数的extern “C”不倾轧限定:

这里写图片描述

举例说明:

#include <iostream>#include <string.h>using namespace std;extern "C"{    void func(int a){cout<<"a = "<<a<<endl;}}int main(void){    int a = 10;    func(a);    return 0;}

由于定义时不倾轧,而调用时倾轧,经过C++编译器,其代码成为:

#include <iostream>using namespace std;/*定义时设置成不倾轧,在编译时,其函数名依旧相同*/void func(int a){cout<<"a = "<<a<<endl;}int main(void){    int a = 10;    /*经C++编译器,函数名变为倾轧后的函数名*/    func_i(a);    return 0;}

这样的代码是不能够完成编译的。而要上面的函数能够正常调用,要么定义与调用时均遵循默认的倾轧,要么均设置为不倾轧,做法如下:

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

将函数声明与函数定义时,都设置为不倾轧即可。当然均不设置,采用默认也是可以编译通过的。但是声明与定义两部分只要任意一个设置为倾轧,另一个设置为不倾轧,编译就不能通过。

测试结果如下:

声明中不加extern “C”,编译出错:
这里写图片描述
声明中加extern “C”,编译不出错:
这里写图片描述

总之一句话,倾轧是C++为了实现函数重载而设计的,不倾轧的extern “C”则是为了兼容C而后实现的。我们编程一般犯不着对自定义的C++函数设置。


0 0