1.命名空间
1.1 为什么要使用命名空间
在C++中,我们会学习到大量的变量,函数,或者类,而这些的名称都将存于全局作用域中(即头文件,或者自己自定义函数于全局作用域中)。所以在我们写代码的过程中就难以避免的出现名称的冲突,比如:
#include <stdio.h>
void max1(int a,int b)
{
return a>b?a:b;
}
int max1=0;
int main()
{
printf("%d\n",rand);
return 0;
}
那么为了解决这种冲突问题,C++引入了命名空间,目的也是对标识符的名称进行本地化,以避免命名冲突或者名字污染。
1.2 如何定义一个命名空间
当我们定义一个命名空间时,我们需要使用namespace关键词,并且在后面写上命名空间的名字,然后接一对{},最后在{}中写下命名空间中的成员即可,比如下段代码。
namespace Date
{
int year=0;
int month=0;
int day=0;
int Add(int x,int y)
return x+y;
struct Day
{
int val;
struct Day* next;
}
//我们看到在命名空间中,我们不仅可以定义变量,还可以定义函数与结构体。
}
而除此之外,在命名空间中,我们也可以再定义一个命名空间(即我们可以嵌套定义命名空间),比如
namespace N1
{
int a;
int b;
int Add(int left, int right)
{
return left + right;
}
namespace N2
{
int c;
int d;
int Sub(int left, int right)
{
return left - right;
}
}
}
看到这里,或许会有人问:假如我在写代码的过程中定义了一个命名空间叫做N1,我的同学在写代码的过程中也定义了一个N1的命名空间。现在如果把我俩的项目合在一起的话,那个命名空间N1的存在情况是怎样的?会不会产生报错呢?
namespace N1
{
int a;
int b;
int Add(int left, int right)
{
return left + right;
}
namespace N2
{
int c;
int d;
int Sub(int left, int right)
{
return left - right;
}
}
}
namespace N1
{
int Mul(int left, int right)
{
return left * right;
}
}
在我们运行后发现程序并没有报错,说明系统允许命名空间重名。而其真实情况是,编译器在遇到重名的命名空间时会将其合并在同一个命名空间中,所以在我们写大工程的过程中,我们并不需要考虑自己的命名空间是否与他人(或者系统)重名!
1.3 如何使用命名空间
命名空间的使用有三种形式
- 加命名空间名称及域作用限定符(::)
- 使用using将命名空间中某个成员引入
- 使用using namespace 命名空间名称引入
命名空间的存在就好比一堵围墙,将其中的成员都“围”了起来。现在如果我们想使用其中的成员的话,我们必须要使用一定的“手段”才可以进行访问,下面我将这三种方法一一讲解。
namespace N1
{
int a=0;
int Mul(int left, int right)
{
return left * right;
}
}
1.3.1 加命名空间名称及域作用限定符
int main()
{
printf("%d\n", N1::a);
return 0;
}
我们可以看到当我们想使用N1命名空间中的成员a时,我们可以使用命名空间+域作用限定符(::)+成员进行访问。这种做法相当于你告诉编译器a这个成员在N1命名空间中,所以在编译器进行编译运行的时候,它就会直接去该命名空间。
1.3.2 使用using namespace 命名空间名称引入
using namespce N;
int main()
{
printf("%d\n", N::a);
printf("%d\n", b);
Add(10, 20);
return 0;
}
在写主函数前,我们可以使用using namespace 命名空间的方式进行引入。在编译器进行编译的时候会将该命名空间进行展开(在未使用using namespace前,我们可以认为N1中的a,b等成员被封装在N1的命名空间中,所以当我们直接使用a,b时,编译器会报错,因为编译器不会自动地去N1命名空间中寻找。),这么做带来的好处是如果我们想多次使用命名空间中的成员的话,我们并不需要多次输入命名空间名称,比较便利。但是凡事都有双面性,命名空间展开也会带来一些Bug,比如
using namespace N1;
void a(int x, int y)
{
return x * y;
}
int main()
{
int number1 = 1;
int number2 = 12;
a(number1, number2);
return 0;
}
当我们运行后,我们会发现
一般在写小程序的时候我们可以自觉地避免这种问题的发生,但是当我们写大项目的时候,我们所定义的命名空间中的某些变量不可避免地会去他人的发生重名,所以这种方法十分建议在平时练习时使用。
1.3.3 使用using将命名空间中某个成员引入
有人可能会问:如果我既不想频繁的写命名空间名称,同时也想解决命名重名的问题,该怎么办呢?那我们就可以选择using将命名空间中一些我们经常使用的变量进行引入。比如std中的cout,endl等
using N1::b;//我们选择将N1中的b成员进行引入
int main()
{
printf("%d\n", N1::a);
printf("%d\n", b);
return 0;
}
1.4 注意
- 在我们定义了一个命名空间后,其是存储在全局中,而成员也是一个全局变量(所有函数都可以使用,只要命名空间的引用正确)。
2.C++的输入&输出
在学习C语言中,我们学到的第一个C程序是
#include <stdio.h>
int main()
{
printf("Hello world!\n");
return 0;
}
这就像是C向世界的第一声问候,那么C++对这个世界的问候是什么呢?
#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{
cout<<"Hello world!!!"<<endl;
return 0;
}
- 当我们使用cout和endl的时候,我们必须要包含头文件(就跟我们在使用C语言的printf时,我们必须要包含<stdio.h>头文件一样。
- <<是流插入运算符,>>是流提取运算符
除此以外,C++的输入输出还有很多特性
#include <iostream>
using namespace std;
int main()
{
int a=0;
double b=0;
char c=0;
cin>>a;
cin>>b>>c;
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
cout<<"c="<<c<<endl;
return 0;
}
看到这里或许会有人问,如果我想让b输出的时候保留三位小数那应该怎么办呢。其实cout和cin可以实现,但是实现过程要比printf要麻烦,所以当我们想保留规定位数时,我们可以使用printf函数(毕竟C++也是兼容C的嘛)。
|