您的位置:首页 > 编程语言 > C语言/C++

C++基础,名字空间、C++函数重载的实现原理

2017-07-28 08:43 344 查看


C++类和对象

我们知道C语言是面向过程的编程语言,而C++是一门面向对象(OPP)的编程语言。 

面向对象是一种程序设计范型,也是一种程序开发的方法。而对象指的是类的实例,将对象作为程序的基本单元,将程序和数据封装在里面,以提高软件的重要性、灵活性和扩展性。 

C++的发展历史 


C几乎是C++的一个子集,所以C语言支持的语法在C++基本都支持并需要使

。C plus plus(C++),所以在C的基础上又有了很多新的特性。 

详见下图

名字空间 http://blog.chinaunix.net/uid-26404477-id-3088450.html
名字空间域是随标准C++而引入的。它相当于一个更加灵活的文件域(全局

域),可以用花括号把文件的一部分括起来,并以关键字namespace开头给它

起一个名字:
namespace name1
{
int a = 0;
} n
amespace name2
{
int a = 1;
} n
ame1::a = 3;
name2::a = 4;


::是作用域解析符

namespace std // std是C++标准库的名字空间

{

// 标准库成员



名字空间域用来解决全局命名冲突的问题.

标准C++库中的所有组件都是在一个被称为std的名字空间中声明和定义的。在采用标准C++的平台上使用标准C++库中的组件,只要写一个using指示符:

using namespace std;

就可以直接使用标准C++库中的所有成员。

或者 std::成员 的方式也可以使用C++库里的成员。 

函数重载

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

在同一作用域类,一组函数的函数名相同,参数列表不同(个数不同/类型不
同),返回值可同可不同

看下面的一个例子,来体会一下:实现一个打印函数,既可以打印int型、也可以打印字符串型。在C++中,我们可以这样做:
#include<iostream>
using namespace std;

void print(int i)
{
cout<<"print a integer :"<<i<<endl;
}

void print(string str)
{
cout<<"print a string :"<<str<<endl;
}

int main()
{
print(12);
print("hello world!");
return 0;
}









 

通过上面代码的实现,可以根据具体的print()的参数去调用print(int)还是print(string)。
上面print(12)会去调用print(int),而print("hello
world")会去调用print(string)

试想如果没有函数重载机制,如在C中,你必须要这样去做:为这个print函数取不同的名字,如print_int、print_string。这里还只是两个的情况,如果是很多个的话,就需要为实现同一个功能的函数取很多个名字,如加入打印long型、char*、各种类型的数组等等。这样做很不友好!

类的构造函数跟类名相同,也就是说:构造函数都同名。如果没有函数重载机制,要想实例化不同的对象,相当麻烦!

操作符重载,本质上就是函数重载,它大大丰富了已有操作符的含义,方便使用,如+可用于连接字符串等!调用重载函数时,函数返回值类型不在参数匹配检查之列。因此,若两个函数的参数个数和类型都相同,而只有返回值类型不同,则不允许重载。

调用重载函数时,函数返回值类型不在参数匹配检查之列,若两个函数参数个数,类型都相同,只有返回值类型不同,则不允许重载。

函数的重载与带默认值的函数一起使用时,有可能引起二义性的问题。
在函数调用时,如果给出的实参和形参类型不相符,c++的编译器会自动地做类型转换工作。如果转换成功,则程序继续执行,在这种情况下,有可能产生不可识别的错误。

以上数点中尤其注意:

1、为什么返回值类型不同不可以构成重载呢?

     因为有的函数虽然有返回值类型,但不与参数表达式运算,而作一条单独的语句。

2、怎样会引起二义性?

     函数的重载与带默认值(缺省)的函数一起使用时有可能引起二义性:

例如:                     

void circle(int r=0,int x=0,int y=0);

void circle ( int r );//当进行调用时,编译系统无法确定调用哪一个函数。

3、在函数调用时,如果给出的实参类型和形参类型不相符,C++编译器会自动做出类型转换工作。若转换成功,则会产生不可识别的错误。

例如:

void fa(int x);

void fa(long x);

虽然这两个函数满足重载条件,但如果用下面代码去调用,就会出现上述错误。

                            int c=fa(5.56);

这是因为编译器无法确定将5.56转化成int还是long类型。

二、重载的底层原理

       其实C++函数重载底层实现原理是C++利用name mangling(倾轧)技术,来改名函数名,区分参数不同的同名函数。
       C++中,这三个函数如果在主函数中被调用选择哪一个,由编译器自身决定。那么不同编译器底下函数的命名风格又是怎样的呢?
      
#include<iostream>
using namespace std;
int Add(int a, int b)
{
return a + b;
}
double Add(double a, double b)
{
return a + b;
}
int main()
{
int m = 10;
int n = 20;
double a=16.3;
double b=15.3;
cout << Add(m,n) << endl;
return 0;
}


 这段代码两个函数Add参数的类型不同,在windows环境下和Linux环境下编译器编译完成后的函数名的命名风格也就不同。
  以下是在VS2013编译器下编译完成map文件中的函数名,可以看出Addd命名发生改变,
一般是 ?[函数名]@@YAHHH@z(只用函数名和YA后面的格式和@z是固定的,其余都不定,它的命名实为返回值的参数类型。(H—int,N—double,X—void)




同样在linux环境下,这段代码中函数的命名也发生改变,一般是_Z[函数名长度][函数名][参数列表的类型首字母]。



 总的来说:源文件通过编译后,将相同函数名,改变成一定的、可区分的格式,去除了函数在调用时的二义性,从而实现函数的重载。 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: