🔥博客主页: 小羊失眠啦.
🎥系列专栏:《C语言》 《数据结构》 《Linux》《Cpolar》
??感谢大家点赞👍收藏?评论??
C语言是结构化和模块化的语言,适合处理较小规模的程序
。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(object oriented programming:面向对象
)思想,支持面向对象的程序设计语言应运而生。 1982年,Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念,发明了一 种新的程序语言。命名为C++。因此:C++是基于C语言而产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计。
1979年,贝尔实验室的本贾尼等人试图分析 unix 内核的时候,试图将内核模块化,于是在C 语言的基础上进行扩展,增加了类的机制,完成了一个可以运行的预处理程序,称之为C with classes。
C++的历史版本:
阶段 | 内容 |
---|---|
C with classes | 类及派生类、公有和私有成员、类的构造和析构、友元、内联函数、赋值运算符 重载等 |
C++ 1.0 | 添加虚函数概念,函数和运算符重载,引用、常量等 |
C++ 2.0 | 更加完善支持面向对象,新增保护成员、多重继承、对象的初始化、抽象类、静 态成员以及const成员函数 |
C++ 3.0 | 进一步完善,引入模板,解决多重继承产生的二义性问题和相应构造和析构的处 理 |
C++98 | C++标准第一个版本,绝大多数编译器都支持,得到了国际标准化组织(ISO)和美 国标准化协会认可,以模板方式重写C++标准库,引入了STL(标准模板库) |
C++03 | C++标准第二个版本,语言特性无大改变,主要:修订错误、减少多异性 |
C++05 | C++标准委员会发布了一份计数报告(Technical Report,TR1),正式更名 C++0x,即:计划在本世纪第一个10年的某个时间发布 |
C++11 | 增加了许多特性,使得C++更像一种新语言,比如:正则表达式、基于范围for循 环、auto关键字、新容器、列表初始化、标准线程库等 |
C++14 | 对C++11的扩展,主要是修复C++11中漏洞以及改进,比如:泛型的lambda表 达式,auto的返回值类型推导,二进制字面常量等 |
C++17 | 在C++11上做了一些小幅改进,增加了19个新特性,比如:static_assert()的文 本信息可选,Fold表达式用于可变的模板,if和switch语句中的初始化器等 |
C++20 | 自C++11以来最大的发行版,引入了许多新的特性,比如:**模块(Modules)、协程(Coroutines)、范围(Ranges)、概念(Constraints)**等重大特性,还有对已有 特性的更新:比如Lambda支持模板、范围for支持初始化等 |
C++23 | 制定ing |
C++一共有63个关键字,其中有32个是C语言中的关键字
asm | do | if | return | try | continue |
---|---|---|---|---|---|
auto | double | inline | short | typedef | for |
bool | dynamic_cast | int | signed | typeid | public |
break | else | long | sizeof | typename | throw |
case | enum | mutable | static | union | wchar_t |
catch | explicit | namespace | static_cast | unsigned | default |
char | export | new | struct | using | friend |
class | extern | operator | switch | virtual | register |
const | false | private | template | void | true |
const_cast | float | protected | this | volatile | while |
delete | goto | reinterpret_cast |
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化, 以避免命名冲突或名字污染。
例如:
#include<stdio.h> #include<stdlib.h> int rand = 10; int main() { printf("%d", rand); return 0; }
在这段程序中,不引用头文件
#include <stdlib.h>
是可以正常运行的,但引用后程序就会报错,这是什么原因呢?因为rand
在<stdlib.h>
中已有了定义,这里报了重定义的错误。
命名空间分割了全局命名空间,其中每一个命名空间是一个作用域。域是一种空间概念,常见的域有:局部域、全局域、类域、命名空间域,域会影响访问和生命周期。
命名空间的定义由两部分构成:首先是关键字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 namespace 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
来表示全局作用域中的aN: :a
来表示定义在N中的a
注意:
如果命名空间没有展开,编译器默认是不会搜索命名空间中的变量,去访问变量是访问不到的。
访问的优先级:局部域 > 全局域
嵌套的命名空间同时是一个嵌套的作用域,它嵌套在外层命名空间的作用域中。嵌套的命名空间中的名字遵循的规则与往常类似:内层命名空间声明的名字将隐藏外层命名空间声明的同名成员。在嵌套的命名空间中定义的名字只在内层命名空间中有效,外层命名空间的代码想要访问它必须在名字前添加限定符。
namespace N
{
int a = 10;
namespace N1
{
int a = 20;
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是C++标准库的命名空间,如何展开std使用更合理呢?
using namespace std;
即可,这样就很方便。using namespace std;
展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型、对象、函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模 大,就很容易出现。所以建议在项目开发中使用,像std::cout
这样使用时指定命名空间例如:using std::cout
展开常用的库对象、类型等方式。#include<iostream>
using namespace std;
int main()
{
int a = 0;
double b = 0;
cin >> a >> b;//流提取运算法
cout << a << " " << b << endl;//流插入运算法 endl相当于换行
cout << a << " " << b << '\n';
return 0;
}
我们在项目中要经常使用 cout
和 endl
,每次指定命名空间很不方便,直接展开会全部暴露,有冲突风险,我们可以指定展开来解决问题。
using std::cout;
using std::endl;
说明:
- 使用**cout标准输出对象(控制台)和cin标准输入对象(键盘)**时,必须
包含< iostream >头文件
以及按命名空间使用方法使用std。cout
和cin
是全局的流对象,endl
是特殊的C++符号,表示换行输出,他们都包含在包含 < iostream >头文件中。<<
是流插入运算符,>>
是流提取运算符。- 使用C++输入输出更方便,不需要像 printf和scanf 输入输出时那样,需要手动控制格式。
C++的输入输出可以自动识别变量类型
。
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
#include<iostream>
using namespace std;
void Test(int a = 0)
{
cout << a << endl;
}
int main()
{
Test();//没有传参时,使用参数的默认值a=0
Test(1);//传参时,使用指定的实参a=1
}
- 全缺省参数(全默认参数)–所有参数都给了缺省值
void Test(int a = 10, int b = 20, int c = 30) { cout << "a = " << a ; cout << " b = " << b ; cout << " c = " << c << endl; } int main() { Test(1, 2, 3); Test(1, 2); Test(1); Test(); return 0; }
全缺省参数在传参时,参数是按照从左往右的顺序进行缺省的,不能跳着缺省,例如:
Func(1, ,3)
,让第一个形参和第三个形参都使用传递值,而让第二个参数使用缺省值,这种做法是不被允许的。
- 半缺省参数 – 部分的参数给了缺省值
void Test(int a , int b = 20, int c = 30) { cout << "a = " << a ; cout << " b = " << b ; cout << " c = " << c << endl; } int main() { Test(1, 2, 3); Test(1, 2); Test(1); return 0; }
注意: