【C++】穿越时光隧道,拾贝史海遗珍,轻启C++入门之钥,解锁程序之奥秘(首卷)

文章目录

一、C++的发展历史

1. 发展历史

C++的起源可以追溯到1979年,当时BjarneStroustrup(本贾尼·斯特劳斯特卢普,这个翻译的名字不同的地⽅可能有差异)在⻉尔实验室从事计算机科学和软件⼯程的研究⼯作。⾯对项⽬中复杂的软件开发任务,特别是模拟和操作系统的开发⼯作,他感受到了现有语⾔(如C语⾔)在表达能⼒、可维护性和可扩展性⽅⾯的不⾜
   1983年,BjarneStroustrup在C语⾔的基础上添加了⾯向对象编程的特性,设计出了C++语⾔的 雏形,此时的C++已经有了类、封装、继承等核⼼概念,为后来的⾯向对象编程奠定了基础。这⼀年该语⾔被正式命名为C++
   在随后的⼏年中,C++在学术界和⼯业界的应⽤逐渐增多。⼀些⼤学和研究所开始将C++作为教学和研究的⾸选语⾔,⽽⼀些公司也开始在产品开发中尝试使⽤C++。这⼀时期,C++的标准库和模板等特性也得到了进⼀步的完善和发展
   C++的标准化⼯作于1989年开始,并成⽴了⼀个ANSI和ISO国际标准化组织的联合标准化委员会。1994年标准化委员会提出了第⼀个标准化草案。在该草案中,委员会在保持斯特劳斯特卢普最初定义的所有特征的同时,还增加了部分新特征在完成C++标准化的第⼀个草案后不久,STL(StandardTemplateLibrary)是惠普实验室开发的⼀系列软件的统称
   它是由AlexanderStepanov、MengLee和DavidRMusser在惠普实验室⼯作时所开发出来的。在通过了标准化第⼀个草案之后,联合标准化委员会投票并通过了将STL包含到C++标准中的提议。STL对C++的扩展超出C++的最初定义范围。虽然在标准中增加STL是个很重要的决定,但也因此延缓了C++标准化的进程
   1997年11⽉14⽇,联合标准化委员会通过了该标准的最终草案。1998年,C++的ANSI/IS0标准被投⼊使⽤

2.版本更新

3.学习C++时的资料查找链接

链接一:https://legacy.cplusplus.com/reference/

链接二、https://zh.cppreference.com/w/cpp

链接三、https://en.cppreference.com/w/

说明:

  1. 链接一是我们之前在C阶段介绍过的资料查找网站,这个网站不是C++官方文档,标准也只更新到C++11,但是以头⽂件形式呈现,内容⽐较易看好懂,我们前期学习时使用功能它来查资料就差不多够了
  2. 后两个链接分别是C++官方文档的中⽂版和英⽂版,信息很全,更新到了最新的C++标准,但是相⽐第⼀个不那么易看,如果想要查找C++比较新的标准就可以使用后两个链接,如果没有这个需求就直接用链接一,比较好查信息

二、C++的第一个程序

C++兼容C语⾔绝⼤多数的语法,所以C语⾔实现的helloworld依旧可以运⾏,C++中需要把定义⽂件代码后缀改为.cpp,vs编译器看到是.cpp就会调⽤C++编译器编译,linux下要⽤g++编译,不再是gcc
   我们先在用C语言方式写一个hello world,我们来对比着学习:

cpp 复制代码
//test.cpp
#include <stdio.h>

int main()
{
	printf("hello world!");
	return 0;
}

C++有⼀套⾃⼰的输⼊输出,C++版本的helloworld是这样写的:

cpp 复制代码
//test.cpp
#include <iostream>
using namespace std;

int main()
{
	cout << "hello world!" << endl;
	return 0;
}

上面代码中的什么iostream、namespace、std、cout、endl看不懂没有关系,我们这里就是作一个引子,下面我们正式介绍C++的入门基础,目标就是在文章结束前我们要能够彻底理解这段代码

三、命名空间

1.命名空间的价值

命名空间即namespace关键字,是C++提出的一个新概念,它是为了避免大量变量、函数与类名称的冲突,使⽤命名空间的⽬的是对标识符的名称进⾏本地化,以避免命名冲突或名字污染
   比如C语言项⽬类似下⾯程序这样的命名冲突是普遍存在的问题,C++引⼊namespace就是为了更好的解决这样的问题

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int rand = 10;

int main()
{
	printf("%d\n", rand);
	return 0;
}

如果我们运行上面的程序会出现什么呢?编译器一定会报错,这是为什么呢?这里就设计到了命名冲突,我们之前在C语言篇章学习过预处理,我们知道在预处理阶段,会展开所有头文件,本质就是将头文件的内容全部拷贝进我们的程序
   而在stdlib.h这个头文件中就包含了一个函数rand(),这个时候如果我们又来定义一个全局变量rand,那么就会"重定义",为了解决类似这样的问题,C++引入了命名空间

2.命名空间的定义与特性总结

定义命名空间的格式:将namespce关键字写在最前面,随后写上命名空间的名称,然后接⼀对{}即可,{}中即为命名空间的成员。命名空间中可以定义变量/函数/类型等,如下:

cpp 复制代码
namespace TL
{
	int rand = 10;

	int Add(int x, int y)
	{
		return x + y;
	}

	struct ListNode
	{
		int data;
		//C++中结构体可以不加struct直接使用
		ListNode* next;
	};
}

在上面的命名空间中,它的名称就是TL,在命名空间中我们什么都可以定义,那么命名空间的定义有什么好处呢?它怎么解决命名冲突的呢?
   这就要提到命名空间的本质了, 它的本质是定义出⼀个域,这个域跟全局域各⾃独⽴,不同的域可以定义同名变量,也就是它将我们的变量函数这些东西从全局域独立出来了,不受全局域的限制,这样它们就可以同名了
   那么C++中有哪些域呢? C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量/函数/类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了
   它们几个域的区别就是:局部域和全局域除了会影响编译查找逻辑,还会影响变量的⽣命周期,命名空间域和类域不影响变量⽣命周期,只会影响编译查找逻辑,也就是当去全局查找变量时,命名空间和类中的成员就不会被查找,但是它们成员的生命周期和作用域没有被改变,只改变了查找逻辑
   那么就以之前举的例子,我们在全局域有一个rand函数,在namespace命名空间中有一个名为rand的整型变量,当我在主函数中直接写出rand时,是指rand函数,还是指我们命名空间中定义的那个整型呢?
   这个就涉及到刚刚说到过的编译查找逻辑了,默认我们会优先从局部去查找对应的变量,如果局部没有就会去全局找对应的变量,如果都找不到就会报错,命名空间和类中的成员就不会被查找
   那么怎么让编译器既不去局部找,也不去全局找,而是去我们的命名空间查找变量呢?我们可以使用作用域限定符::(两个英文冒号),将它加到对应变量或函数前就可以指定我们要指定访问的域,可以是命名空间域,也可以是类域,类域我们在类和对象再说,现在先演示一下怎么使用作用域限定符访问命名空间域:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

namespace TL
{
	int rand = 10;
}

int main()
{
    //没有域限定访问符默认在局部先找
    //局部没有找到就去全局找到了rand函数
	int ret = rand();
	printf("ret = %d\n", ret);
	//直接使用域访问限定符,直接访问
	//命名空间TL中的rand变量
	printf("rand = %d\n", TL::rand);
	return 0;
}

在上面的例子中,我们就使用了域访问限定符直接访问命名空间TL,我们也大致清楚了编译器的查找规则,首先看有没有域访问限定符,有的话就直接访问对应的域,如果没有就先从局部域查找,找不到就去全局域查找
   那么是不是命名空间的变量和函数等等成员只能通过域访问限定符访问呢?其实不是,有多种方法,我们在第3个小节命名空间的使用方式总结,这里我们继续讲解命名空间最后两个特性
   namespace只能定义在全局,但是可以嵌套定义,比如这个小功能是由2个人一起实现的,我们就可以定义一个大的命名空间存放这个功能,其中又包含了这两个人分别的实现,如下:

cpp 复制代码
#include <stdio.h>

namespace func1
{
	namespace TL
	{
		int rand = 10;
		//......
	}
	namespace WJH
	{
		int rand = 20;
		//......
	}
}


int main()
{
	//访问TL中的成员rand
	printf("%d\n", func1::TL::rand);
	//访问WJH中的成员rand
	printf("%d\n", func1::WJH::rand);
}

最后,如果项⽬⼯程中多⽂件中定义的同名namespace会认为是⼀个namespace,不会冲突,也就是说如果多个.cpp文件中有同名的namespace,那么这些命名空间被看做一个命名空间,这里不再演示了,知道就好

3.命名空间的使用方式

我们在上面说到过,默认编译查找时不会在命名空间去查找,因为命名空间改变了成员的查找逻辑,不会到命名空间⾥⾯去查找,所以如果我们要使⽤命名空间中定义的变量/函数,有三种⽅式:

指定命名空间域访问

项⽬中推荐这种⽅式,也就是我们在上面就介绍过的,使用域访问限定符::,这样准确地指定了命名空间就几乎不会有命名冲突的问题,如下:

cpp 复制代码
printf("%d\n", TL::rand);

使用using将命名空间中某个成员展开

当我们使用using将命名空间中某个成员展开后,那么我们在访问这个成员时,就不需要域访问限定符而直接访问了,项⽬中经常访问的不存在冲突的成员推荐这种⽅式,这种方式只会展开部分成员,属于比较折中的处理方式,如下:

cpp 复制代码
 // using将命名空间中某个成员展开
using TL::rand;
//之后使用rand成员不用再加域访问限定符就可以访问
//但是一旦这个成员被展开,就会直接暴露在全局
//可能会与全局中的函数与变量出现命名冲突
//所以项⽬中经常访问的不存在冲突的成员推荐这种⽅式

使用using展开命名空间中全部成员

那么命名空间的所有成员都会暴露在全局域之下,所以项⽬不推荐,冲突⻛险很⼤,⽇常⼩练习程序为了⽅便可以使⽤,如下:

cpp 复制代码
//将TL所有成员全部展开
using namespace TL;

4.using namespace std解惑

我们将命名空间并不是无用的,在实践中的使用非常多,目前我们接触不到,但是我们现在就可以分析一下之前写的C++第一个程序中的using namespace std的作用
   我们学习了命名空间后,可以看出来std是一个命名空间的名字,std这个命名空间里存放的就是C++标准库,并且很明显这种用法属于我们之前讲过的第三种使用方式,使用using展开命名空间中全部成员都展开暴露在全局域
   这样C++标准库的内容我们就可以直接访问了,许多人都只以为using namespace std是写C++程序必备的语句,但是没有深究它的作用和原理,其实本质上它就是一个命名空间,只是这个命名空间存放的是C++标准库的内容

四、C++输入&输出简介

要使用C++的输入输出系统首先要包含一个头文件,它是InputOutputStream的缩写,是标准的输⼊、输出流库,定义了标准的输⼊、输出对象,其中就包括了C++自己的一套输入输出操作符cout和cin
   这里说明一点,在C++中,包含头文件没有.h后缀,不要写错了,我们自定义的头文件也可以没有.h的后缀,但是为了能一眼看出来文件的作用,我们自己写头文件时还是建议写上后缀名

1.输入

cin

在进行输入时,我们通常使用cin,它是istream类的对象,它主要⾯向窄字符的标准输⼊流,它存放在标准输入输出库中,在前面我们也谈到过,C++标准库的内容都存在于一个名为std的命名空间中
   如果我们不展开std这个命名空间中的内容,就不能直接去访问cin这个操作符,要指定它的类域,即std::cin,一般写小型练习我们一般会把std直接展开,如下:

cpp 复制代码
using namespace std;

但是我们要注意⼀般⽇常练习中我们可以using namespace std,实际项⽬开发中不建议using namespace std

流插入操作符

我们要完成输入操作的话,除了cin还不够,我们还需要使用流插入操作符>>,在C语言中它是右移操作符,在C++中被复用为了流插入操作符
   流插入操作符可以自动识别变量的类型,不用像scanf一样再指定%d,%c之类的格式,它能够自动识别类型其实是使用了函数重载,我们后面会讲到,但是最重要的是C++的流能更好的⽀持⾃定义类型对象的输⼊输出,我们在类和对象章节讨论
   那么说了这么多,>>该怎么使用呢?大致使用方法如下:

cpp 复制代码
#include <iostream>
using namespace std;

int main()
{
	int n = 0;
	//注意不要把符号写反了
	cin >> n;
	return 0;
}

2.输出

cout和endl

cout 是ostream类的对象,它主要⾯向窄字符的标准输出流,endl 是⼀个函数,流插⼊输出时,相当于插⼊⼀个换⾏字符加刷新缓冲区,现在可以简单理解为它有换行的作用
   cout和endl也属于C++标准库,存放在命名空间std中,它们要结合流提取操作符进行使用,所以接着我们来看看流提取操作符

流提取操作符

流插入操作符是>>,流提取操作符就是<<,注意不要把它们搞混了,流提取操作符也可以自动识别类型,不用像C语言那样指定格式,使用方法如下:

cpp 复制代码
#include <iostream>
using namespace std;

int main()
{
	cout << "hello world!" << endl;
	return 0;
}

这就是C++中的输入输出,比C语言稍微简洁一点,但是它们的强大在普通类型上还体现不出来,要等之后我们学习到类和对象才能感受到它们的真正魅力
   当然IO流涉及类和对象,运算符重载、继承等很多⾯向对象的知识,这些知识我们还没有讲解,所以这⾥我们只能简单认识⼀下C++IO流的⽤法,后⾯我们会有专⻔的⼀个章节来细节IO流库
   最后我们再强调一下,C++是兼容C语言的,所以我们在C++中也可以使用printf和scanf来输入输出,在VS中就算没有包含stdio.h,也可以使用
   因为VS将它们包含在了iostream这个头文件中,但是其它编译器有可能会报错,要看编译器的具体实现,报错的时候包含一下stdio头文件即可,在有时候printf和scanf甚至更好用,后面我们会提到
   那么今天C++入门基础的知识(上)就分享到这里啦,欢迎点赞三连,有什么问题欢迎给我私信,我会尽快回复

bye~

相关推荐
数据小爬虫@4 分钟前
利用Java爬虫获取店铺所有商品:技术实践与应用指南
java·开发语言·爬虫
运维@小兵10 分钟前
SpringBoot入门之创建一个Hello World项目
java·spring boot·后端
qiu_shi_12 分钟前
1.2.1-2部分数据结构的说明02_链表
数据结构
V+zmm1013416 分钟前
婚庆摄影小程序ssm+论文源码调试讲解
java·微信小程序·小程序·毕业设计
程序员徐师兄16 分钟前
Java基于微信小程序的私家车位共享系统
java·微信小程序·私家车车位共享·私家车车位共享系统
codeydj16 分钟前
记录一次线上因kafka宕机而导致java服务cpu飙升的情况
java·分布式·kafka
司空良22 分钟前
docker从下载到Python项目打包到容器中运行(解决下拉超时问题)
python·docker
Mirilla25 分钟前
华为iotda sdk发送消息无法更新quickstartpython问题解决
java·服务器·前端
郭wes代码29 分钟前
Python安装(新手详细版)
开发语言·python