目录
命名空间
C++存在大量的变量、函数和类的名称等标识符,可能会导致很多冲突,使用命名空间对标识符的名称进行本地化,防止命名冲突或名字污染。
例如我们有一下的使用场景:
int rand = 0;
此时单独定义rand变量时,不存在问题。但是在下面情况下便会出现命名冲突:
#include <stdlib.h>
int rand = 0;
由于stdlib库中有rand函数,因此与全局作用域下的rand变量发生命名冲突!因此引入了命名空间的概况,便于我们对变量等标识符更好的处理。
命名空间的定义
定义命名空间,需要使用namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中为命名空间的成员。例如:
namespace name
{
//命名空间成员变量
}
命名空间的使用情况:
1.普通的命名空间
namespace N1
{
int a;
int Add(int x, int y)
{
return x + y;
}
}
2.命名空间的嵌套
namespace N2
{
int a;
int Add(int x, int y)
{
return x + y;
}
namespace N3
{
int a;
int Sub(int x, int y)
{
return x - y;
}
}
}
3.同一个工程中允许存在多个相同的命名空间,编译器最后会合成在同一个命名空间中
namespace N1
{
int b; //ok
int c = 10; //ok
//c = 20; //error
}
注意:(1)一个命名空间定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中
(2)命名空间中只允许声明和初始化,不对赋值(如上图所示)
命名空间的使用
命名空间我们该如何使用呢?
通常我们使用作用域限定符::来取某个命名空间中的成员
namespace N
{
int a = 10;
int b = 20;
int Add(int x, int y)
{
return x + y;
}
}
命名空间的使用有三种方式:
- 加命名空间名称及作用域限定符(指定作用域,可以最好的隔离,但使用麻烦)
int main()
{
printf("%d
",N::a);
return 0;
}
- 使用using将命名空间中的成员引入(展开常用的)
using N::d;
int main()
{
printf("%d
",N::a);
printf("%d
",b);
return 0;
}
- 使用using namespace命名空间名称引入(全部展开,隔离失效)
using namespace N;
int main()
{
printf("%d
",a);
printf("%d
",b);
Add(10, 20);
return 0;
}
C++输入&输出
#include<iostream>
using namespace std;
int main()
{
cout<<"hello world!"<<endl;
return 0;
}
以上程序是利用C++的输出函数cout打印hello world。
C++的特有输入输出:cin & cout
- cin >>:>>为流提取运算符,将键盘输入的变量从缓冲区提取到cin中
- cout <<:<<为流插入运算符,将所有你需要输出的变量或者字符串流入到cout中,让cout负责输出
- cin和cout与scanf和printf不同的是,可以自动识别变量的类型,不用再指定变量类型(%d、%c、%f等)
缺省参数
缺省参数的定义
缺省参数表示在声明或定义函数时,为函数的参数指定一个默认值
int Function(int a = 10)//这里为a指定了一个默认值10
{
return a + 1;
}
如果在调用时没有指定实参,则采用默认值;如果在调用时有传入实参,则使用指定实参。
// 没有传参,使用参数的默认值
Function();
// 传参时,使用实参
Function(20);
缺省参数的分类
- 全缺省参数
void TestFunc(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
以下的调用都是有意义的:
TestFunc();
TestFunc(3);
TestFunc(2, 3);
TestFunc(1, 2, 3);
但是不能用以下方法调用:
TestFunc(, 2, );
注意:传进去的实参是从右往左依次传入,必须连续
- 半缺省参数
void TestFunc(int a, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
注意:(1)半缺省参数必须从右往左来给出,不能间隔给出
(2)缺省参数不能在函数声明和定义中同时出现
(3)缺省值必须是常量或者全局变量
(4)C语言不支持缺省参数
函数重载
函数重载的概念
int Add(int left, int right)
{
return left+right;
}
double Add(double left, double right)
{
return left+right;
}
long Add(long left, long right)
{
return left+right;
}
int main()
{
Add(10, 20);
Add(10.0, 20.0);
Add(10, 20);
return 0;
}
但下面两个函数不属于函数重载:
short Add(short left, short right)
{
return left+right;
}
int Add(short left, short right)
{
return left+right;
}
因为他们参数个数、类型、顺序相同,函数的返回类型不同,不能说明函数重载!!!
函数重载的原理
为什么c++支持函数重载而c语言却不支持呢?
我们知道,c++程序运行,需要经过以下几个阶段:预处理,编译,汇编,链接
阶段 | 工作内容 | 生产的文件类型 |
预处理 | 头文件展开、宏替换、条件编译、去掉注释 | *.i |
编译 |
检查语法、生产汇编代码 | *.s |
汇编 | 汇编代码转换成二进制的机器码 | *.o |
链接 | 合并段表、符号表的合并和重定位 | *.out |
而这里的重点在链接阶段,
通过我们C语言阶段学习的编译链接,我们可以知道,如果我们在a.cpp中调用了某个函数(Add函数),但是函数(Add函数)是在b.cpp中定义的,所以函数(Add函数)的地址就会在b.obj中。在a中就无法找到,那么怎么办呢?
链接阶段就是专门处理这种问题,链接器看到a.o调用函数,但是没有Add的地址,就会到b.o的符号表中找函数的地址,然后链接到一起。
那么在链接的时候,面对Add函数,链接器该使用什么名字取找呢?
此时由于windows下的visual stdio编译器下的命名规则特别复杂且繁琐,我们在linux环境下进行验证。
(1)验证C语言是否支持重载
首先在Linux环境下创建test.c、func.c、func.h三个文件
使用C语言编译器gcc运行test.c和func.c会出现错误:
- 因为C语言在编译的时候,两个重载函数的函数名相同,在func.o的符号表中存在歧义和冲突。
- 其次在链接的时候也存在歧义和冲突,因为他们是直接使用函数名在标识和查找,而重载函数的函数名相同。
因此C语言不支持重载函数!!!
(2)验证C++是否支持重载
使用C++编译器g++运行test.c和func.c
因此C++支持重载函数!!!
那么思考缺省参数是否为重载函数?
int F(int a);
int F(int a = 10);
当然不构成,因为他们的参数类型一致!