C/C++教程

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

本文主要是介绍C++函数重载与重载原理:命名倾轧,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

==
转自:https://blog.csdn.net/lichen18848950451/article/details/71006642==

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

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

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

/要是放在C语言中,这段代码必然是编译不通过,而在C++中是合乎重载原则的/
#include

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
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

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
#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

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
#include"func.h"

using namespace std;

int main(void){
int a = 10;

func(a);

return 0;

}
/func.c/
#include
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++函数设置。

这篇关于C++函数重载与重载原理:命名倾轧的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!