C++ 入门全指南:从发展史到第一个程序,命名空间 + 输入输出手把手讲

C++ 入门全指南:从发展史到第一个程序,命名空间 + 输入输出手把手讲
目录

一、C++的发展历史

1.发展历史

2.C++的版本更新

3.C++的参考文档

二、C++的学习建议

1.C++的应用领域:

2.学习书籍推荐:

三、C++的第一个程序

四、命名空间

1.namespace的价值:

2.namespace的定义:

1)使用namespace来命名空间,以及使用命名空间(详解见注释):

2)命名空间的嵌套使用

3)多文件定义的命名空间问题

3.namespace命名空间的使用:

1)指定命名空间访问

2)using将命名空间中某个成员展开

3)展开命名空间中全部成员

五、C++输入&输出

前言:
经过前段学习的C语言和数据结构初阶,现在我们迎来了c++板块的学习。c++的学习通常来说是比较困难的,但是有前面C语言的坚实基础,对我们来说也可以是平步上青云~本篇博文先给大家介绍一下c++的发展历史、版本更新以及命名空间的问题。

一、C++的发展历史

1.发展历史

1.起源(1979-1983):C++的起源可以追溯到1979年,当时Bjarne Stroustrup(本贾尼·斯特劳斯特卢普)在贝尔实验室从事计算机科学和软件⼯程的研究⼯作。面对项⽬中复杂的软件开发任务,特别是模拟和操作系统的开发⼯作,他感受到了现有语言(如C语言)在表达能力、可维护性和可扩展性方面的不足。1983年,Bjarne Stroustrup在C语言的基础上添加了面向对象编程的特性,设计出了C++语言的雏形,此时的C++已经有了类、封装、继承等核心概念,为后来的面向对象编程奠定了基础。这⼀年该语言被正式命名为C++。在随后的几年中,C++在学术界和⼯业界的应用逐渐增多。⼀些大学和研究所开始将C++作为教学和研究的首选语言,而⼀些公司也开始在产品开发中尝试使用C++。这⼀时期,C++的标准库和模板等特性也得到了进⼀步的完善和发展。2.标准化(1989-1994):C++的标准化⼯作于1989年开始,并成立了⼀个ANSI和ISO(International StandardsOrganization)国际标准化组织的联合标准化委员会。1994年标准化委员会提出了第⼀个标准化草案。在该草案中,委员会在保持斯特劳斯特卢普最初定义的所有特征的同时,还增加了部分新特征。在完成C++标准化的第⼀个草案后不久,STL(Standard Template Library)是惠普实验室开发的⼀系列软件的统称。它是由Alexander Stepanov、Meng Lee和David R Musser在惠普实验室⼯作时所开发出来的。在通过了标准化第⼀个草案之后,联合标准化委员会投票并通过了将STL包含到C++标准中的提议。STL对C++的扩展超出C++的最初定义范围。虽然在标准中增加STL是个很重要的决定,但也因此延缓了C++标准化的进程。3.正式投入使用(1997):1997年11⽉14⽇,联合标准化委员会通过了该标准的最终草案。1998年,C++的ANSI/IS0标准被投入使用。

C++的诞生是为了弥补C语言的不足之处!所以部分的C语言依然能够在c++程序中成功运行~

2.C++的版本更新

C++的版本更新基本上在5年左右更新一次,大家可以看下图来了解C++的历代版本与更新内容~

3.C++的参考文档

Reference - C++ Referencehttps://legacy.cplusplus.com/reference/C++ reference - cppreference.comhttps://en.cppreference.com/w/cpp.html

说明:第⼀个链接不是C++官方文档,标准也只更新到C++11,但是以头文件形式呈现,内容比较易看好懂。后两个链接分别是C++官方文档的英文版,信息很全,更新到了最新的C++标准,但是相比第⼀个不那么易看;这两个文档各有优势,我们结合着使用。

二、C++的学习建议

1.C++的应用领域:

C++ 的应用领域服务器端、游戏(引擎)、机器学习引擎、音视频处理、嵌入式软件、电信设备、金融应用、基础库、操作系统、编译器、基础架构、基础工具、硬件交互等很多方面都有。大型系统软件开发。如编译器、数据库、操作系统、浏览器等等。音视频处理。常见的音视频开源库和方案有 FFmpeg、WebRTC、Mediasoup、ijkplayer,音视频开发最主要的技术栈就是 C++。PC 客户端开发。一般是开发 Windows 上的桌面软件,比如 WPS 之类的,技术栈的话一般是 C++ 和 QT,QT 是一个跨平台的 C++ 图形用户界面(Graphical User Interface,GUI)程序。服务端开发。各种大型应用网络连接的高并发后台服务。这块 Java 也比较多,C++ 主要用于一些对性能要求比较高的地方。如:游戏服务、流媒体服务、量化高频交易服务等游戏引擎开发。很多游戏引擎就都是使用 C++ 开发的,游戏开发要掌握 C++ 基础和数据结构,学习图形学知识,掌握游戏引擎和框架,了解引擎实现,引擎源代码可以学习 UE4、Cocos2d-x 等开源引擎实现嵌入式开发。嵌入式把具有计算能力的主控板嵌入到机器装置或者电子装置的内部,通过软件能够控制这些装置。比如:智能手环、摄像头、扫地机器人、智能音响、门禁系统、车载系统等等,粗略一点,嵌入式开发主要分为嵌入式应用和嵌入式驱动开发。机器学习引擎。机器学习底层的很多算法都是用 C++ 实现的,上层用 python 封装起来。如果你只想准备数据训练模型,那么学会 Python 基本上就够了,如果你想做机器学习系统的开发,那么需要学会 C++。测试开发 / 测试。每个公司研发团队,有研发就有测试,测试主要分为测试开发和功能测试,测试开发一般是使用一些测试工具 (selenium、Jmeter 等),设计测试用例,然后写一些脚本进行自动化测试,性能测试等,有些还需要自行开发一些测试用具。功能测试主要是根据产品的功能,设计测试用例,然后手动的方式进行测试。

2.学习书籍推荐:

C++ Primer:

主要讲解语法,经典的语法书籍,前后中期都可以看,前期如果自学看可能会有点晦涩难懂,能看懂多少看懂多少,中后期作为语法字典,非常好用。
STL源码剖析:

主要从底层实现的角度结合STL源码,庖丁解牛式剖析STL的实现,是侯捷老师的经典之作。可以很好的帮助我们学习别人用语法是如何实现出高效简洁的数据结构和算法代码,如何使用泛型封装等。让我们不再坐井观天,闭门造车,本书课程上⼀半以后,中后期可以看。
Effctive C++:

这本书也是侯捷老师翻译的,本书有的⼀句评价,把C++程序员分为看过此书的和没看过此书的。本书主要讲了55个如何正确高效使用C++的条款,建议中后期可以看⼀遍,工作1-2年后再看⼀遍,相信会有不⼀样的收获。

三、C++的第一个程序

C++兼容C语言绝大多数的语法,所以C语言实现的hello world依旧可以运⾏,C++中需要把定义文件代码后缀改为.cpp,vs编译器看到是.cpp就会调用C++编译器编译,linux下要用g++编译,不再是gcc。
//这里使用的是test.cpp #include<stdio.h> int main() { printf("Hello World\n"); return 0; }

然而C++有自己的一套输入输出设备,所以在C++中的hello world应该是这样:

#include<iostream> using namespace std; int main() { cout << "hello world\n" << endl; return 0; }

四、命名空间

1.namespace的价值:

在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的⽬的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。

c语言项目类似下面程序这样的命名冲突是普遍存在的问题,C++引入namespace就是为了更好的解决这样的问题。

// 命名冲突 #include<stdio.h> #include<stdlib.h> int rand = 10; int main() { // 编译报错:error C2365: “rand”: 重定义;以前的定义是“函数” printf("%d", rand); return 0; }

报错如图:

因为库函数stdlib.h中已经定义了rand,所以C语言就无法运行这串代码了。但在C++的输入输出中就可以用namespace来解决这个问题。

2.namespace的定义:

• 定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接⼀对{}即可,{}中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。

• namespace本质是定义出⼀个,这个域跟全局域各自独立,不同的域可以定义同名变量,所以下面的rand不在冲突了。

• C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量/函数/类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。局部域和全局域除了会影响编译查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量生命周期。

• namespace只能定义在全局,当然他还可以嵌套定义。

• 项目⼯程中多文件中定义的同名namespace会认为是⼀个namespace,不会冲突。

• C++标准库都放在⼀个叫std(standard)的命名空间中。

1)使用namespace来命名空间,以及使用命名空间(详解见注释):

#include<stdio.h> #include<stdlib.h> namespace A //A是命名空间的名字,⼀般开发中是⽤项⽬名字做命名空间名 { // 命名空间中可以定义变量/函数/类型 int rand = 10; int ADD(int a,int b) { return a + b; } struct Node { struct Node* next; int val; }; }//注意这里没有分号!! int a = 1; int main() { //编译器语法查找确认规则是默认先局部查找->全局查找->没有找到就报未声明的标识符这个错误 //::域作用限定符,这里指定作用域,就直接按这个域去找->没有找到就报未声明的标识符这个错误 int a = 0; printf("%d\n", a); printf("%d\n", ::a);//::前面不加域就是默认全局域!不会到命名空间里查找! printf("%p\n", rand);//这里的rand是stdlib.h中的指针,要用%p访问 printf("%d\n", A::rand);//这里访问的就是A域中的rand printf("%p\n", A::ADD); printf("%d\n", A::ADD(1, 2));//这里是直接调用该函数 //A::struct Node node;//这是错误的 struct A::Node node; return 0; }

这个程序的运行结果如下:

!!!这里需要注意的是命名空间域不会影响变量的生命周期(是一个全局的生命周期)。

2)命名空间的嵌套使用

#include<stdio.h> #include<stdlib.h> //命名空间的嵌套使用 namespace A { namespace a { int rand = 10; int ADD(int a,int b) { return a + b; } } namespace b { int rand = 20; int ADD(int a, int b) { return (a + b) * 10; } } } int main() { printf("%d\n", A::a::rand); printf("%d\n", A::b::rand); printf("%d\n", A::a::ADD(1, 2)); printf("%d\n", A::b::ADD(1, 2)); return 0; }

程序的运行结果如下:

命名空间的嵌套可以嵌套很多层,但是不推荐,因为那样写起来也很不方便~

我们在项目中经常会用到命名空间的嵌套,大家可以看下面的图片来理解一下

3)多文件定义的命名空间问题

多文件中可以定义同名namespace,他们会默认合并在一起,就像同一个namespace一样。下面给大家简单演示一下~

test.cpp

#include<stdio.h> #include<stdlib.h> #include"Stack.h" #include"Queue.h" int main() { st::stack s1; st::Init(&s1); st::Push(&s1, 1); st::Push(&s1, 2); st::Pop(&s1); queue::Queue q1; queue::Init(&q1); queue::Push(&q1, 1); queue::Push(&q1, 2); queue::Pop(&q1); return 0; }

stack.h

#pragma once #include<stdio.h> #include<stdlib.h> #include<assert.h> #include<stdbool.h> namespace st { typedef int STDataType; typedef struct stack { STDataType* arr; int top; int capacity; }stack; void Init(stack* ps); void Push(stack* ps, STDataType x); void Pop(stack* ps); }

stack.cpp

#include"Stack.h" namespace st { void Init(stack* ps) { //...... } void Push(stack* ps, STDataType x) { //... } void Pop(stack* ps) { //... } }

queue.h

#pragma once #include<stdio.h> #include<stdlib.h> #include<assert.h> #include<stdbool.h> namespace queue { typedef int QDataType; typedef struct QueueNode { QDataType data; struct QueueNode* next; }QueueNode; typedef struct Queue { QueueNode* phead; QueueNode* ptail; }Queue; void Init(Queue* pq); void Push(Queue* pq, QDataType x); void Pop(Queue* pq); }

queue.cpp

#include"Queue.h" namespace queue { void Init(Queue* pq) { //...... } void Push(Queue* pq, QDataType x) { //... } void Pop(Queue* pq) { //... } }

3.namespace命名空间的使用:

编译查找一个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以下面程序会编译报错。所以我们要使用命名空间中定义的变量/函数,有三种方式:

指定命名空间访问,项目中推荐这种方式。

using将命名空间中某个成员展开,项⽬中经常访问的不存在冲突的成员推荐这种方式。

展开命名空间中全部成员,项目不推荐,冲突风险很⼤,日常小练习程序为了方便推荐使用。

1)指定命名空间访问

#include<iostream>//C++中不需要带.h #include<algorithm> int main() { int a[5] = { 4,1,2,5 }; // 指定命名空间访问 std::sort(a, a + 4); return 0; }

2)using将命名空间中某个成员展开

#include<iostream> #include<algorithm> //命名空间中成员部分展开 using std::cout; using std::cin; int main() { int a, b; cin >> a >> b;//输入 cout << a << " " << b << '\n';//输出 return 0; }

在这里我们可以看到部分展开了之后cout就不需要在前面加东西了,但是cin前面还是需要加std的。这个展开方式还是推荐在大型项目中使用的~

3)展开命名空间中全部成员

#include<iostream> #include<algorithm> //命名空间中成员全部展开 using namespace std; int main() { int a, b; cin >> a >> b;//输入 cout << a << " " << b << '\n';//输出 return 0; }

这里就是把std中的所有成员全部展开,在做算法题或者小程序没有命名冲突的情况下可以使用,而且使用起来比较方便,但是在大型项目中就不推荐使用了~

五、C++输入&输出

• <iostream> 是 Input Output Stream 的缩写,是标准的输入、输出流库,定义了标准的输入、输出对象。

• std::cin 是 istream 类的对象,它主要面向窄字符(narrow characters (of type char))的标准输入流。

• std::cout 是 ostream 类的对象,它主要面向窄字符的标准输出流。

• std::endl 是一个函数,流插入输出时,相当于插入一个换行字符加刷新缓冲区。

• << 是流插入运算符,>> 是流提取运算符。(C 语言还用这两个运算符做位运算左移 / 右移)

• 使用 C++ 输入输出更方便,不需要像 printf/scanf 输入输出时那样,需要手动指定格式,C++ 的输入输出可以自动识别变量类型 (本质是通过函数重载实现的,这个以后会讲到),其实最重要的是 C++ 的流能更好的支持自定义类型对象的输入输出。

• IO 流涉及类和对象,运算符重载、继承等很多面向对象的知识,这些知识我们还没有讲解,所以这里我们只能简单认识一下 C++ IO 流的用法,后面我们会有专门的一个章节来细节 IO 流库。

• cout/cin/endl 等都属于 C++ 标准库,C++ 标准库都放在一个叫 std (standard) 的命名空间中,所以要通过命名空间的使用方式去用他们。

• 一般日常练习中我们可以 using namespace std,实际项目开发中不建议 using namespace std。

• 这里我们没有包含 < stdio.h>,也可以使用 printf 和 scanf,在包含<iostream>间接包含了。vs 系列编译器是这样的,其他编译器可能会报错。

举例说明:

#include<iostream> int main() { int i = 100; double b = 1.11000; //任何变量都会转换成字符串,插入到流中 //cout会自动识别 std::cout << i << "\n"; std::cout << b << "\n"; std::cout << "hello world" << std::endl;//end line std::cout << "hello" << " " << "world" << std::endl; std::cin >> i >> b;//cin是输入 std::cout << i << " " << b << std::endl;//cout是输出 return 0; }

换行用 '\n' 的效率会比使用 endl 更高,除此以外,cout和cin的效率其实也是不如printf和scanf的,但是printf和scanf不能处理类(也就是更复杂的类型),而C++的IO流支持复杂类型的输入输出。在C++的IO流中我们可以通过取消同步流的操作来解决这个问题,代码如下:

#include<iostream> using namespace std; int main() { // 在io需求⽐较⾼的地⽅,如部分⼤量输⼊的竞赛题中,加上以下3⾏代码 // 可以提⾼C++IO效率 ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); return 0; }

结语:
这篇博文纠分享到这里了,后续还会有1~2篇文章来分享C++入门的知识,好让大家更全面的从C语言转换到C++来,C++的学习是一个比较平滑的过程,中间的难度提升不会一下就特别多,是一个慢慢进步的过程。如果文章对你有帮助的话,欢迎评论,点赞,收藏加关注,感谢大家的支持。

Read more

【数据结构副本篇】顺序表 链表OJ

【数据结构副本篇】顺序表 链表OJ

🏝️专栏:【数据结构实战篇】 🌅主页:f狐o狸x 目录         一、移除元素         1.1 题目分析         1.2 解题代码 二、删除有序数组中的重复项         2.1 题目分析         2.2 解题代码 三、合并两个有序数组         3.1 题目分析                 3.2 解题代码 四、移除链表元素         4.1 题目分析         4.2 解题代码 五、反转一个链表         5.1 题目分析         5.2 解题代码         六、链表的中间节点         6.1 题目分析

By Ne0inhk

Uncaught TypeError: Cannot read properties of undefined (reading ‘xxx‘):从报错根源到根治方案(前端/后端通用)

Uncaught TypeError: Cannot read properties of undefined (reading ‘xxx’):从报错根源到根治方案(前端/后端通用) 引言:被“undefined”支配的恐惧 如果你是开发者,大概率在控制台见过这句红色报错——“Uncaught TypeError: Cannot read properties of undefined (reading ‘xxx’)”(或后端类似“Cannot read field ‘xxx’ of null”)。据2024年《开发者调试痛点调研》显示,这类“空值访问错误”占前端日常报错的32%,后端接口处理报错的28%,平均每次调试耗时15-30分钟,尤其在复杂业务场景(如嵌套数据渲染、异步接口依赖)中,往往需要逐层排查才能定位根因。 但多数开发者解决这类报错时,只停留在“加个if判断”

By Ne0inhk
Flutter for OpenHarmony: Flutter 三方库 fixnum 解决鸿蒙 Web 与原生端 64 位大整数精度失真难题(精准计算护卫)

Flutter for OpenHarmony: Flutter 三方库 fixnum 解决鸿蒙 Web 与原生端 64 位大整数精度失真难题(精准计算护卫)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在进行 OpenHarmony 的跨平台开发时,你可能会遇到一个诡异的 Bug:同样的 64 位长整数(如 Int64),在鸿蒙原生(Native)模式下运行正常,但编译为 Flutter Web 模式在浏览器运行时,数值却发生了精度漂移或溢出。 1. 产生原因:JavaScript 原生的数字类型实质上是 64 位浮点数,它能安全表示的最大整数只有 53 位( 2 53 − 1 2^{53}-1 253−1)。 2. 后果:大额订单 ID、高精度的金融分位值、或是底层硬件的 64 位地址位,在

By Ne0inhk