C++相对于C语言而言支持函数重载是其极大的一个特点,相信在使用C语言的时候大家如果要写一个实现两个整型数据相加的函数还要写一个浮点型数据相加的函数,那么这两个函数的名字绝对不可以一样,这样无疑在我们使用这个函数的时候增加了复杂性,但是在C++中我们却可以很好的解决这个问题,因为在C++中函数是支持重载的也就是说两个函数的函数名可以一样,这样并不会出现函数名重定义的问题,但是我们在使用的时候也要遵守一些规定,这些规定我们会在接下来的讨论中提到,下面我们就来分析在C++中函数是如何实现函数的重载的。
在这里我们用C语言和C++分别写两个函数,通过函数的符号表来观察函数名在经过编译之后究竟是什么形式的
下面就是我们的测试代码:
1 #include<iostream> 2 3 using namespace std; 4 5 6 7 int Add(int x, int y) 8 9 { 10 11 int z = 0; 12 13 z = x + y; 14 15 return z; 16 17 } 18 19 20 21 double Add(double x, double y) 22 23 { 24 25 double z = 0; 26 27 z = x + y; 28 29 return z; 30 31 } 32 33 34 35 int main() 36 37 { 38 39 cout<<Add(1,3)<<endl; 40 41 cout<<Add(1.5,3.5)<<endl; 42 43 return 0; 44 45 }
在VS2008的编译环境下:
我们生成.map文件,然后可以查看函数在经过编译之后的函数名称为下图所示:
不难发现上图中函数命名的一些规律(当然这个规律只是片面的针对于VS2008编译环境):
1.以“?”开始和以”@Z”结尾
2.函数的名称紧接“?”之后
3.在函数明德后面分别是函数返回值类型修饰符、参数列表中的参数的类型修饰符
下面我们把这个相同的函数改为c语言的代码
代码如下:
1 //#include<iostream> 2 3 //using namespace std; 4 5 #include<stdio.h> 6 7 8 9 10 11 int Add(int x, int y) 12 13 { 14 15 int z = 0; 16 17 z = x + y; 18 19 return z; 20 21 } 22 23 24 25 double Add(double x, double y) 26 27 { 28 29 double z = 0; 30 31 z = x + y; 32 33 return z; 34 35 } 36 37 38 39 int main() 40 41 { 42 43 //cout<<Add(1,3)<<endl; 44 45 //cout<<Add(1.5,3.5)<<endl; 46 47 return 0; 48 49 }
这时我们编译的话就会出现错误:
这里告诉我们函数名出现重定义
那么这是为什么呢?
这时我们注释掉一个函数然后编译后查看.map文件,查看函数重命名之后的名称
这里我们可以发现函数在编译之后重命名的名称仅仅只是在函数名称的前面加上了一个"_"(下划线),这样我们就不难分析了,C和C++编译的时候对函数的重命名机制是完全不一样的
1.C语言中仅仅只是在函数的名称的前面加上了"_"(下划线)
2.C++有自己的命名修饰规则,他会根据函数的参数列表中变量的类型等进行相应的类型修饰
虽然C++支持函数的重载但是我们在使用的时候也要注意以下几点:
1.函数的重载只是出现在同一作用域,例如假如两个工程里的函数名称相同,但是他们也不是函数的重载
2.函数名相同,函数的参数列表不同,返回值可同可不同,为什么函数返回值可同可不同呢?
这是因为在不同的编译环境下对于函数名称的修饰并不是相同的,下面就是在Linux环境下函数编译后重命名的形式:
仔细观察不难发现在Linux环境下的函数重命名的一些规则:
1.以“_Z”z作为开头,紧随其后的数字是函数名称的单词的个数
2.函数的名称后面有函数的参数列表中参数的类型修饰符,i是int型,d是double型
通过以上的阐述相信大家可以对C++中为什么可以实现函数重载有了清晰的认识,那么我们也就不难回答为什么在C++中调用被C编译过后的函数应该在前面加上 extern "C" 声明了。
这是因为我们当前是处于c++语言环境,这个时候我们如果不指定要调用的那个函数是用C语言编译的函数,那么当前在C++文件中编译时就会报错说是该函数是一个无法解析的外部符号,因为在编译运行的时候我们当前的程序会从符号表中去找相应的函数名,可是C++和C编译后生成的符号表中函数的名是不同的,那么这个函数也就是一个无法解析的外部符号了,但是当你用extern “C” 指明该函数是用C语言编译的函数,那么当前代码在编译运行的时候就会从用C语言编译的那个符号表文件中去查找相应的函数名,这样整个程序的编译运行费也就没有问题了。