命名空间
命名空间的作用:
看一个实际项目开发中可能遇到的问题,A程序猿在用C写代码是使用了n这个变量,B程序猿在用C写代码时也使用了n这个变量,当两人将项目整合后运行时便会因为使用同名变量而出错,命名空间的出现就是为了解决这样的问题。
命名空间常用吗?
随便去找一个C++程序,你很难不看到这句代码:
using namespace std;
其中的std就是C++提供的标准库,我们使用的各种函数等都封装在其里面,使用的时候必须要写这句话,那学过C的小伙伴就要问了,为啥C语言不需要呢?这就要谈及到命名空间的本质了。
命名空间的本质:
创建一个命名空间就是开辟了一个全新的作用域 ,因为C++只会排查全局作用域中是否有同名变量,所以我们就可以在不同于全局的作用域定义和全局中变量名一样的变量了。
C语言的库都是全局作用域中的,而C++的std标准库不在全局中,所以我们需要使用各种方法访问它。
命名空间的创建:
namespace Yxz
{
int nums1 = 0;
int Add(int a, int b)
{
return a + b;
}
}
如上面代码所示,创建了一个名为Yxz的空间,里面放了一个nums1变量和Add函数。
namespace是C++的一个关键字,其作用便是可以用来定义命名空间。
命名空间可嵌套:
//Yxz命名空间嵌套一个Typ空间
namespace Yxz
{
int nums1 = 0;
int Add(int a, int b)
{
return a + b;
}
namespace Typ
{
int num1 = 0;
int Sub(int a, int b)
{
return a - b;
}
}
}
两个嵌套的命名空间里面的变量是可以同名的!(前提:两命名空间名称不同)
命名空间可同名:
//Yxz命名空间嵌套一个Typ空间
namespace Yxz
{
int nums1 = 0;
int Add(int a, int b)
{
return a + b;
}
namespace Typ
{
int num1 = 0;
int Sub(int a, int b)
{
return a - b;
}
}
}
namespace Yxz
{
int num2 = 1;
}
两个命名空间的名称是可以一样的,编译器会将同名的命名空间整合到一起,也就是说虽然是在不同的位置定义的命名空间,但是其实开辟的是同一个作用域,那么一定注意,两个同名的命名空间中一定不要有同名变量,不然整合后同一片作用域就会出现同名的变量;
命名空间的使用:
之前说了,命名空间实际就是开辟了一个全新的作用域,那我们如何使用这个作用域里面的东西呢?
1:全部展开
//Yxz命名空间嵌套一个Typ空间
namespace Yxz
{
int nums1 = 0;
int Add(int a, int b)
{
return a + b;
}
namespace Typ
{
int num1 = 0;
int Sub(int a, int b)
{
return a - b;
}
}
}
using namespace Yxz;//展开Yxz命名空间
using namespace Typ;
using是C++的一个关键字,很好理解,中文是使用的意思,这里就是展开。
上面的代码是将整个命名空间展开到全局作用域,这样我们就可以使用被展开的空间里面所有的东西了。
在这里得强调一点,这里的展开并不是将空间的东西拿到全局中去,而是让你能够在全局中使用!
在上面的代码中,我将Yxz和Typ这两个空间都展开了,注意到Typ是嵌套在Yxz里面的,所以在展开Typ的时候一定要先展开Yxz,正如上面所说的,不展开Yxz我们没权限使用Typ,就不能直接展开Typ;所以下面的代码就是错误的。
namespace Yxz
{
int nums1 = 0;
int Add(int a, int b)
{
return a + b;
}
namespace Typ
{
int num1 = 0;
int Sub(int a, int b)
{
return a - b;
}
}
}
//using namespace Yxz;//展开Yxz命名空间
using namespace Typ;//错误,无法访问无法展开
还有一个问题,如果展开的空间中有变量或函数和全局冲突,怎么办?
我用的vs2013,对于变量它的处理是全局作用域变量优先,对于函数,则报错,因为编译器不知道使用哪一个,当场懵逼报错,后面的函数重载会涉及。
2:部分展开
注意到我们也许不需要一个命名空间中的所有东西,而且全部展开还会有函数名冲突导致的错误,部分展开就可以让我们随意选择需要展开的变量。
上面的代码的意思是我需要Yxz空间的nums1变量,但是在使用全部展开后,发现Add函数同名报错,这时候就可以使用部分展开:
using Yxz::nums1
::是范围限定符,空间名+::就可以访问空间中的任何一个变量,使用using将访问的变量展开,就是这么简单。
3:不展开,直接访问:
在部分展开的结尾说了 空间名+::可以访问空间中的东西,自然有下面的用法:
注意,上面是将Yxz空间全部展开,然后再main函数中去直接访问Add函数,有没有发现一个很牛逼的东西,Add同名报错的问题没了!使用这种方式就算展开后全局作用域中有同名函数也不用怕了,因为我指定了要使用哪个函数,编译器就不会因为不知道使用哪个而懵逼了。
C++输入&输出
C++提供cout标准输出和cin标准输入两个流,要使用他们必须包含头文件<iostream>以及展开std标准命名空间;
使用方法如下:
int main()
{
int select = 0;
cin >> select;
if (select == 1)
{
cout << "hehe" << endl;
}
else
{
cout << "haha" << endl;
}
return 0;
}
endl相当于\n的作用,就是换行;
注意到C++输入输出是不需要格式控制的。
缺省参数
全缺省函数
int Add(int x=10, int y=20)//x,y为缺省参数
{
return x + y;
}
缺省参数就是在声明函数或者定义函数时设置形参的时候给定一个默认值,那么这个带有默认值的参数就是缺省参数。
使用缺省参数就支持下面的用法:
int Add(int x=10, int y=20)
{
return x + y;
}
int main()
{
cout << Add() << endl;
return 0;
}
在调用函数的时候可以不传对应位置的参数,这个位置的参数就使用给定的默认值。Add(,30)这样的用法是错误的。
半缺省函数
半缺省函数就是最少有一个缺省参数但又不能全缺省的函数。
注意在定义参数的时候必须从右往左定义,并且必须连续定义,目的是为了歧义的出现。缺省值只需要在声明和定义的一个地方出现,不能同时出现,是为了排除这种错误情况:在声明和定义中同一变量的初始值可能设定不同,编译器就无法确定使用哪个缺省值;
int Add(int x, int y=20)
{
return x + y;
}
int main()
{
cout << Add() << endl;
return 0;
}
注意点罗列:
- 缺省参数必须从左往右给,并且必须连续;
- 缺省参数只需要在声明和定义中出现一次,不能同时出现;
- 缺省值必须是常量或者全局变量,常量表达式也可;
函数重载
函数重载是C++为了能够使用同名函数而产生的一种语法。
形成函数重载的必要条件:
- 函数名相同;
- 函数参数的个数,类型,顺序不同;
使用函数重载是一件简单的事情,但是我们得思考思考,为什么C++支持函数重载呢?
这里在Linux平台可以看的更清楚:
我们先看C语言的处理:
可以看到我在test.c文件中定义了一个Add函数,在使用C编译器对其进行编译生成的汇编代码中看到,Add经过编译后仍然以Add存在,假设我在定义一个Add函数,那么它经过编译也将以Add存在,那么在链接的时候编译器就不知道调用哪个Add函数了,出现链接错误!
C++的处理:
在test.cpp中定义了两个Add函数构成重载,可以看到在通过编译后,两Add函数的名字不一样,就可以区别两个Add函数了,在链接的时候就不会出错了。
这就是C++相交C语言特有的名字修饰,也是为什么支持重载的原因;
通过观察被修饰后的名字可以发现:
- 都是_z开头的
- 3都表示函数名的长度
- ii和iii都是参数的类型
可以看出C++修饰规则并不复杂,因为只需要能够区分同名函数即可,搞的那么复杂也没用。
注意点罗列:
- 要构成重载必须要满足函数名相同,但是参数的种类,个数,类型要不完全相同;
- 支持重载的原因是C++具有名字修饰的特性;
- 返回值不同不能作为重载的条件;
如果文中有任何问题还请读者指出,如果有任何问题可在评论区留言。