🎈个人主页:库库的里昂
✨收录专栏:C++从练气到飞升
🎉鸟欲高飞先振翅,人求上进先读书。
前言
什么是C++
C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(object oriented programming:面向对象)思想,支持面向对象的程序设计语言应运而生。
1982年,Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念,发明了一种新的程序语言。为了表达该语言与C语言的渊源关系,命名为C++。因此:C++是基于C语言而产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计。
C++的发展史
1979年,贝尔实验室的本贾尼等人试图分析unix内核的时候,试图将内核模块化,于是在C语言的基础上进行扩展,增加了类的机制,完成了一个可以运行的预处理程序,称之为C with classes。
语言的发展就像是练功打怪升级一样,也是逐步递进,由浅入深的过程。我们先来看下C++的历史版本。
C++还在不断的向后发展。但是:现在公司主流使用还是C++98和C++11,所有大家不用追求最新,重点将C++98和C++11掌握好,等工作后,随着对C++理解不断加深,有兴趣的小伙伴可以去琢磨下更新的特性。
📋命名空间
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
#include <stdio.h> #include <stdlib.h> int rand = 10; // C语言没办法解决类似这样的命名冲突问题,所以C++提出了namespace来解决 int main() { printf("%d\n", rand); return 0; } // 编译后后报错:error C2365: “rand”: 重定义;以前的定义是“函数”
命名空间定义
命名空间的定义由两部分构成:首先是关键字namespace,后面跟命名空间的名字,然后接一对{},{}中即为命名空间的成员。 命名空间中可以定义变量、函数、类型和其他命名空间。
namespace N1//命名空间的名字 { //定义变量 int rand = 10; //定义函数 int Add(int left, int right) { return left + right; } //定义类型 struct Node { struct Node* next; int val; }; //嵌套命名空间 namespace N2 { int Sub(int left, int right) { return left - right; } } }
注意:
- 一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中。
- 用一个工程中允许出现多个相同名称的命名空间,编译器最后会将它们合并为一个命名空间。
命名空间使用
命名空间的使用有三种方式:
- 加命名空间名称及域作用限定符
namespace N { int a=10; int b=5; } int main() { printf("%d\n", N::a); return 0; }
- 使用 using 将命名空间中某个成员引入
using N::b; int main() { printf("%d\n", N::a); printf("%d\n", b); return 0; }
- 使用 using namespace 命名空间名称引入(展开命名空间)
namespace N { int a=10; int b=5; } int a=20; using namespce N; int main() { printf("%d\n", a); //a不明确,有二义性 printf("%d\n", ::a); //访问全局的a printf("%d\n", N::a); //访问N中的a printf("%d\n", b); return 0; }
N中的成员a 就与全局作用域中的a 产生了冲突。这种冲突是允许存在的,但是要想使用冲突的名字,我们就必须明确指出名字的版本。main函数中所有未加限定的a都会产生二义性错误。
这时我们必须使用域作用限定符(::)来明确指出所需的版本
- : :a来表示全局作用域中的a
- N: :a来表示定义在N中的a
注意:
如果命名空间没有展开,编译器默认是不会搜索命名空间中的变量,去访问变量是访问不到的。
访问的优先级:局部域 > 全局域
命名空间的嵌套
嵌套的命名空间同时是一个嵌套的作用域,它嵌套在外层命名空间的作用域中。嵌套的命名空间中的名字遵循的规则与往常类似:内层命名空间声明的名字将隐藏外层命名空间声明的同名成员。在嵌套的命名空间中定义的名字只在内层命名空间中有效,外层命名空间的代码想要访问它必须在名字前添加限定符。
namespace N { int a = 10; namespace N1 { int a = 20; //将外层作用域的a隐藏了 int b = 15; namespace N2 { int c = N1::b; } } int main() { printf("%d\n", N::N2::c); printf("%d\n", N::N1::a); printf("%d\n", N::a); return 0; }
std命名空间的使用
std是C++标准库的命名空间,如何展开std使用更合理呢?
- 在日常练习中,建议直接using namespace std;即可,这样就很方便。
- using namespace std;展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型、对象、函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模 大,就很容易出现。所以建议在项目开发中使用,像std::cout这样使用时指定命名空间例如: using std::cout展开常用的库对象、类型等方式。
📋C++输入&输出
#include <iostream> using namespace std; int main() { int a = 10; double b = 10.5; cout << a << endl; cout << b << endl; return 0; }
我们在项目中要经常使用 cout 和 endl,每次指定命名空间很不方便,直接展开会全部暴露,有冲突风险,我们可以指定展开来解决问题。
using std::cout; using std::endl;
说明:
- 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件以及按命名空间使用方法使用std。
- cout 和 cin 是全局的流对象,endl 是特殊的C++符号,表示换行输出,他们都包含在包含 < iostream >头文件中。
<<
是流插入运算符,>>
是流提取运算符。- 使用C++输入输出更方便,不需要像 printf和scanf 输入输出时那样,需要手动控制格式。 C++的输入输出可以自动识别变量类型。
📋缺省参数
缺省参数的定义
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
void Func(int a = 5) { cout << a << endl; } int main() { Func(); // 没有传参时,使用参数的默认值 Func(10); // 传参时,使用指定的实参 return 0; }
上面代码在第一次调用 Func() 时,没有传递参数,a 就使用了缺省值。
缺省参数分类
- 全缺省参数 -- 所有参数都给了缺省值
void Func(int a = 10, int b = 20, int c = 30) { cout<<"a = "<<a<<endl; cout<<"b = "<<b<<endl; cout<<"c = "<<c<<endl; } int main() { Func(1,2,3); Func(1,2); Func(1); Func(); return 0; }
全缺省参数在传参时,参数是按照从左往右的顺序进行缺省的,不能跳着缺省,例如:Func(1, ,3) ,让第一个形参和第三个形参都使用传递值,而让第二个参数使用缺省值,这种做法是不被允许的。
- 半缺省参数 -- 部分的参数给了缺省值
void Func(int a, int b = 20, int c = 30) { cout<<"a = "<<a<<endl; cout<<"b = "<<b<<endl; cout<<"c = "<<c<<endl; } int main() { Func(1,2,3); Func(1,2); Func(1); return 0; }
半缺省参数必须从右往左依次来给出,不能间隔着给。
注意:
- 缺省参数不能在函数声明和定义中同时出现,只能出现在函数声明中。
- 缺省值必须是常量或者全局变量。
【C++从练气到飞升】02---C++入门(二)+https://developer.aliyun.com/article/1502583