1. 命名空间
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将
都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行
本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
如下图所示:
C语言是没有办法很好的解决此问题, 因此C++提出了一个新语法,命名空间.
1.1 命名空间定义
定义命名空间需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}即为命名空间成员。
//1.普通的命名空间 //定义了一个叫qk的命名空间-域 namespace qk { //命名空间可以定义函数/变量/类型 int rand = 1;//他们还是全局变量,放到静态区 int sub(int a, int b) { return a - b; } struct node { struct node* next; int data; }; } //命名空间可以嵌套 namespace n1 { int a; int b; int add(int m, int n) { return m + n; } namespace n2 { int c; int d; int sub(int g, int f) { return g - f; } } } //同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。 namespace n3 { int mul(int a, int b) { return a * b; } }
1.2 命名空间使用
首先说一下最常见的一种错误用法,例如:
namespace qk { int a=10; int b=20; int sub(int c,int d) { return c-d; } } int main() { printf("%d",a);//该编译错误,无法识别a }
命名空间的使用有三种方式:
- 加命名空间名称及作用域限定符(::)【安全性高,但使用起来不方便】
int main() { printf("%d",qk::a); return 0; }
- 使用using将命名空间中最常用的成员引入【安全性高,并且可以减少一些麻烦】
using N::b; int main() { printf("%d",b); return 0; }
- 使用using namespace命名空间引入 【安全性低,平常练习可以用用,但做项目不推荐使用】
using namespace qk; int main() { sub(10,20); return 0; }
易错区:
1.在命名空间中定义的成员是全局变量,并放在静态区;
2.命名空间可以初始化,但是不能赋值;
namespace qk { int rand=10;//可以初始化 rand=5;//但是不可以赋值 }
2. C++输入&输出
C++是包含C语言的,因此在C++中同样可以使用C语言输入和输出.
使用cout标准输出和cin标准输入时,必须包含<iostream>头文件以及std标准命名空间;
这里我直接使用using namespace将命名空间展开,平常练习代码推荐使用;
C++中的输入和输出更加方便,可以自动识别类型,小数点后有几位就几位不会向后估读;
endl和C语言中的换行符'\n'作用一样;
<<:流插入运算符,>>:流提取运算符;
3. 缺省参数
3.1 概念
缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参。
3.2 缺省参数分类
- 全缺省参数
//全缺省参数 void qk(int a = 10, int b = 20, int c = 30) { cout << "a=" << a << endl; cout << "b=" << b << endl; cout << "c=" << c << endl; }
- 半缺省参数
//半缺省参数 -- 缺省部分参数 -- 必须从右往左缺省,必须连续缺省 void qk(int a, int b = 20, int c = 30) { cout << "a=" << a << endl; cout << "b=" << b << endl; cout << "c=" << c << endl; }
注意:
- 半缺省参数必须从右往左依次给出,不能间隔着给;
- 缺省参数不能在函数声明和定义同时给出;
- 缺省值必须是常量或者是全局变量;
- C语言不支持;
/这均是错误的写法 void qk(int a = 10, int b, int c) void qk(int a = 10, int b, int c = 30) qk(, 2, ); qk(, , 3); //heap.h(声明) void qk(int a = 10); // heap.c(定义) void qk(int a = 20) {} // 注意:如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那 //个缺省值。
4. 函数重载
自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。
比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前者是“谁也赢不了!”,后者是“谁也赢不了!"
4.1 概念
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数或类型或顺序)必须不同,常用来处理实现功能类似数据类型不同的问题.、
//1.参数类型不同 int Add(int left, int right) { cout << "int Add(int left, int right)" << endl; return left + right; } double Add(double left, double right) { cout << "double Add(double left, double right)" << endl; return left + right; } //2.参数个数不同 void f() { cout << "f()" << endl; } void f(int a) { cout << "f(int a)" << endl; } //3.参数顺序不同 void f(int a, char b) { cout << "f(int a,char b)" << endl; } void f(char b, int a) { cout << "f(char b, int a)" << endl; }
下面函数属于函数重载吗?
//返回值不同,不能构成重载 -- 调用的时候不能区分 short Add(short left, short right) { return left + right; } int Add(short left, short right) { return left + right; }
// 1、缺省值不同,不能构成重载 void f(int a) { cout << "f()" << endl; } void f(int a = 0) { cout << "f(int a)" << endl; } // 2、构成重载,但是使用时会有问题 : f(); // 调用存在歧义 void f() { cout << "f()" << endl; } void f(int a = 0) { cout << "f(int a)" << endl; } int main() { // f(); // 调用存在歧义 f(1); return 0; }
4.2 名字修饰
为什么C++支持函数重载,而C语言不支持函数重载?
在C/C++中,一个程序要运行起来要经历以下几个阶段:预处理,汇编,编译,链接.
1. 实际我们的项目通常是由多个头文件和多个源文件构成,而通过我们C语言阶段学习的编译链接,我们可以知道,【当前a.cpp中调用了b.cpp中定义的Add函数时】,编译后链接前,a.o的目标文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。那么怎么办呢?
2. 所以链接阶段就是专门处理这种问题,链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符号表中找Add的地址,然后链接到一起。
3. 那么链接时,面对Add函数,连接器会使用哪个名字去找呢?这里每个编译器都有自己的函数名修饰规则。
4. 由于Windows下vs的修饰规则过于复杂,而Linux下gcc的修饰规则简单易懂,下面我们使用了gcc演示了这个修饰后的名字。
5. 通过下面我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长度+函数名+类型首字母】。
- 采用C语言编译器编译后结果
结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。
- 采用C++编译器编译后结果
- 结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息
添加到修改后的名字中。也理解了,为什么函数重载要求参数不同,跟返回值没关系。
【拓展学习:C/C++函数调用约定和名字修饰规则】
C/C++ 函数调用约定_狮子的专栏-CSDN博客_c 调用约定
通过这里我们就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持重载。
另外我们也了解了,为什么函数重载要求参数不同,而跟返回值没关系.
5. extern "C"(下一章做重点介绍)
有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern "C",意思是告诉编译器,将该函数按照C语言规则来编译。
注意:在函数前加上"extern C"后,该函数便不能支持重载了。
extern "C" int Add(int a, int b); int main() { Add(1,2); return 0; }