为什么我时不时会看到「珍惜生命,远离 C++」?
写 C++ 快十一个年头了。
这是 2014 年,写 C++ 的第 3 年,和女朋友相遇的第 4 年:
这是写 C++ 的第 8 年,和女朋友(老婆)相遇的第 9 年。
这个问答提问一下子把我的思绪拉入往昔那些使用 C/C++ 的“峥嵘岁月”里。女朋友还是原来的女朋友,而我却不是当初的那个我了。
老实说,这些年用 C/C++ 分别写过大大小小的项目,也研究像 Linux 内核、Nginx、Redis、MySQL 等各种经典 C/C++ 项目,玩的不亦乐乎,常看常新,看到这些经典项目的精彩代码处时有时候喜不自禁,那真是笑看妻子愁何在,漫卷诗书喜欲狂。
贴几张图:
目前人在某大厂做技术专家,感觉在 C++ 方面只是个熟练工,因为要学的东西太多了。且 C++ 这门语言的特点是,你要想写出性能或者不出错的代码,你必须了解 C++ 背后操作系统的各种原理,例如指针,实际是操作系统内存知识。
我用一张图来概括下 C++ 技术栈,虚线框里面为 C++ 开发必备的技能。
看了上文,如果你还没被劝退,能坚持看到这里的同学必须给自己点个赞~
好啦,不调侃了,实话实说,C++ 学到一定程度,很多技术原理就通透了,既可以做底层,也可以往上层发展,转其他语言或者方向也不费劲。总体来说,想比较其他语言,C++ 这门语言的下限很高,上限也很高,投入越大,回报越高。
我是这么学习 C++ 的
算上本科 4 年,硕士 3 年,我接触 C++ 也算十五六年了,分享一下我的 C++ 打怪升级之路。
1. 第一阶段
无论你是科班还是非科班,建议你一定要学好 C 语言,它应该作为你必须掌握好的语言。你要熟悉 C 语言的基本语法,包括:
- C 中几大基元数据类型的用法
- 顺序、条件、循环三大控制语句
- 熟悉掌握数组的用法
- 熟练掌握指针的用法
- 熟练掌握结构体、枚举、联合等数据类型的用法
- 熟练使用常用 C 库函数,如控制台输入输出流、字符串操作、文件操作、时间函数等等
以上阶段算是启蒙阶段,在这个阶段,是基础编程语法的学习,当然,你不仅仅要掌握这些基本语法,你还要反复练习。
这个阶段可以推荐一本非常的图书《C 语言设计 现代方法》,这本书在国外是作为大学教材的,引进到国内翻译的也很棒。
链接:https://pan.baidu.com/s/11lyRZMMlLJC5cBIOHeU0ig
提取码:49nj
2. 第二阶段
如果你想毕业后进大厂,从这时开始学习算法和数据结构。学习算法和数据一定不能急功近利,算法和数据结构的知识一定要作为一个长期的学习目标。
对于非科班的同学,由于没有像科班同学那样接受到系统的学习,一定要找一两本经典书籍系统地学习下常见的算法理论、思想和常用的数据结构知识。等熟悉了这块的知识,再适当地刷一些算法题目或者做一些算法练习。
如果你觉得算法和数据结构很吃力,推荐一下《算法(第四版)》:
链接:https://pan.baidu.com/s/1EQDzYRMhYR5DLCGvdixr_g
提取码:wnfv
面试大厂之前要适当刷一些题目,推荐leetcode 和 《剑指 offer》。
链接:https://pan.baidu.com/s/1GSWKWpcYORLheE4PnW5Aqw
提取码:emvh
3. 第三阶段
如果你是学生,想走 C++ 开发这条路,其实大学四年(研究生三年)说长不长,说短也不短。接下来就挨个补相应的基础知识了。如果你是非科班,也建议自己找点经典的书籍系统地学习一下。下面挨个说一下这几大方面的基础知识。
3.1 C++ 语言学习
如果想系统地学习 C++,请抛弃各种总结经验技巧的面经,逐个掌握 C++ 语言的各个语法,包括但不局限于:
- 指针和引用的概念
- 指针与内存关系
- 程序编译过程,静态链接库和动态链接库
- static、const、#define的用法和区别
- C和C++区别
- 内存中的栈和堆分配
- 面向对象理解
- 访问限定符 public/protected/private
- 构造函数/析构函数/拷贝构造
- 多态
- 虚函数与纯虚函数、虚函数实现机制、虚函数表
- 继承原理、虚继承、菱形继承
- new/delete和malloc/free
- 重载、重写和覆盖
- 类型转换方式
- RAII 与 pimpl 惯用法
- 内存溢出和内存泄漏
- STL标准模板库
- 迭代器、空间配置器理解
- 常用容器特点、用法以及底层实现vector、list、deque、set、map、unorderedmap
时至今日,C++11/14/17 新标准也慢慢成为主流,这块也要熟悉,要熟悉新标准常用的语法与新功能,你一定要熟悉 C++11/14/17 常用的语言特性和类库,这里简单地列一下:
- 左值/右值/std::move/std::forward
- 统一的类成员初始化语法与 std::initializer_list
- 注解标签(attributes)
- final/override/=default/=delete 语法
- auto 关键字
- Range-based 循环语法
- 结构化绑定
- stl 容器新增的实用方法
- std::thread
- 线程局部存储 thread_local
- 线程同步原语 std::mutex、std::condition_variable 等
- 原子操作类
- 智能指针类
- std::bind/std::function
C++11/14 网上的资料已经很多了,C++17 的资料不多,重点掌握的还是 C++11 引入的各种实用特性,这就给读者推荐一些我读过的书:
- 《深入理解 C++11:C++11 新特性解析与应用》
- 《深入应用 C++11:代码优化与工程级应用》
- 《C++17 完全指南》 《Cpp 17 in Detail》
链接:https://pan.baidu.com/s/1V3UXRCzEryRd9cDEcf0naQ
提取码:qnzu
3.2 学习 C++ 相关的开发工具链
学习 C++ 的相关的 IDE 开发环境,Windows上我推荐 Visual Studio,Mac 上可以使用Subline 或者VSCode,如果你最终的程序需要在 Linux 跑,你需要熟悉 cmake/make/gcc/g++/gdb 工具链,vim 的基本操作也要熟悉,但不建议在 vim 中写代码,实际企业级开发也没人会这么做。
这里强调一下,如果可能,至少要熟悉 Visual Studio 和 gdb 调试,调试需要掌握哪些内容呢?
建议掌握:
- 如何启动和结束调试
- 如何添加/删除/启用/禁用断点(包括普通断点、条件断点和数据断点)
- 如何查看当前断点下的调用堆栈
- 如何查看程序运行过程中的线程信息(这块可以放到下文再学)
- 如何查看某个变量的内存值
注意:如果你想胜任 Linux C/C++ 后台开发, 一定要熟练使用 gdb 调试 ,我推荐《gdb 高级调试实战教程》,电子书下载链接:
链接:https://pan.baidu.com/s/1iUqNpUAZ623w-2eBm-mRUg
提取码:zcjs
以下是《gdb 高级调试实战教程》目录:
掌握了一门编程语言加其开发的工具链,你的想法就可以通过动手变为现实了。熟悉了 C/C++ 语言和其相关的开发工具链,接下来你可以根据你的兴趣学习相关的开发知识了。
3.3 学习操作系统的 API 接口
很多人说,操作系统的 API 接口不用刻意学习,根据我个人的经验,我反对这一观点,操作系统的很多 API 涉及到很多操作系统原理和使用技巧,绝非是想用的时候去查一查就可以了。就和开卷考试一样,如果不熟悉,且不说不知道如何查、在哪查,就算查到了,是否可以用好又是另外一回事了。
Linux 系统推荐学习《Linux 系统编程》,Windows 系统推荐《Windows 程序设计(第五版)》和 《Windows 核心编程》。
Linux 系统编程 链接:https://pan.baidu.com/s/12lp_P7--6TgSnRgfZwVWAQ 提取码:gjnv
Windows 程序设计(第五版)
链接:https://pan.baidu.com/s/1xp9kj9nQBMn1b3G3zEUtjw 提取码:s48b
Windows 核心编程 链接:https://pan.baidu.com/s/1ff8lS0tluQGJQblCpkJsHw 提取码:4jnn
学习操作系统的接口不仅是学习相关操作系统 API,同时也是在培养自己的动手和实践能力。常用的操作系统 API 并不多,以多线程相关的为例,Windows 系统提供的线程同步原语常用的有临界区、Event、互斥体、信号量等,Linux 常用的有互斥体、读写锁、信号量和条件变量,掌握这些并不困难,你只要花一周时间挨个地学习练习一下,很快就能掌握。
熟悉了常用操作系统的 API 之后,你就可以自由地写出自己想要的程序了,这个时候处于初级的通透状态,例如,你可以随手写出这样一个功能的程序:
主线程创建一个新的工作线程,等待工作线程获取系统时间后写入文件;主线程从文件中读取时间内容显示出来。
这个例子中,我们用到了创建线程的 API、线程等待与通知 API、获取系统时间的 API、显示到控制台的 API
在 Windows 上,我们用到:
- CreateThread
- WaitForSingelObject
- GetSystemTime
- CreateEvent/SetEvent
- std::cout
在 Linux 上,我们可以使用:
- pthread_create
- pthread_mutex_init/pthread_cond_init/pthread_cond_destroy/pthread_cond_wait
- time
- std::cout
这么一分析是不是觉得一下子清晰起来,因为 C/C++ 这门编程语言不是功能完备性的,如果 C/C++ 的 C 库或者 stl 本身没有提供这些功能,你不得不使用操作系统的 API。
如果你掌握到这个阶段,恭喜你,你已经可以去胜任中小企业的 C/C++ 开发了。所以,我推荐的这条路线,如果你认真学习,保底能让你找到一份小公司的 C++ 开发工作的。
3.4 学习多线程编程
这块与上文有一点重叠,我们再次说一下。多线程知识,你需要掌握理解线程与进程的关系、熟练使用常用的线程同步技术。推荐的一种学习方式,就是找一个开源项目,使用调试器跑起来,然后看看这个进程有多少线程,每个线程在何时被创建,每个线程的作用是什么,线程之间如何通信的。这也是上文建议你熟练掌握调试器的原因。
3.5 学习操作系统原理
操作系统原理无论是面试还是自我提高的五大基础之一,我的建议学习操作系统知识时,不一定要看完所有操作系统书籍,但一定将一些基础概念(如进程、线程、内存模式等)看懂、理清。Tanenbaum.A.S《现代操作系统》是一本讲解操作系统理论不错的书,作者 Tanenbaum.A.S 是 Linux 内核创始人 Linus Torvalds 的老师。
现代操作系统
链接:https://pan.baidu.com/s/11CVFdmB8l09AUqKymPSLwg 提取码:bbdo
你如果还有时间强烈推荐看看俞甲子的《程序员的自我修养:链接、装载与库》。这本书同时涉及到了 Windows 和 Linux 两个操作系统平台,用各种辅助工具剖析了程序从源码到二进制文件再到装载到进程地址空间里面的各个细节,甚至连进程地址空间中的堆结构、栈结构也分析得清清楚楚,同时也分析了 C Runtime(CRT)、glibc 这样的操作系统接口库的原理和执行逻辑,是一本实实在在帮你实战操作系统原理的好书。
程序员的自我修养:链接、装载与库 链接:https://pan.baidu.com/s/1PBZIrYeNPvT5qHbISiuUBw 提取码:bn50
当然,学有余力的同学,可以进一步了解一些关于操作系统的模式(如实模式、保护模式)、系统的启动与初始化、虚拟内存与物理内存、内存分表分页机制、进程与线程的调度算法等知识。
3.6 学习计算机网络和 Socket 编程
学习计算机网络要从以下三个方面学习:
3.6.1 计算机网络理论知识
计算机网络编程你需要掌握基础的如三次握手和四次挥手的过程以及各个状态值,我建议使用 tcpdump 命令实际抓下包就一目了然了,然后就是网络分层,各层的用途,重点熟悉下 TCP/IP 层相关的知识,还有就是 TCP/UDP 的区别,TCP 的滑动窗口机制、拥塞控制算法、TCP 的保序、重传、确认机制。
学习这些知识的时候,一定不要死记硬背,注重理解。我近来面试了一部分学历学校非常好的同学,然而,在问到这块的知识时却大失所望。例如,有的同学只是单纯把三次握手背下来了,我稍微变通一下他就不知道怎么回答了:
- 如果连接一个目标主机不存在的 IP 地址握手过程是怎样的?连接一个目标 IP 存在但是端口号不存在的主机又是怎样的握手过程呢?
- A 机器上的进程与 B 机器上的进程进行网络通信,分别经历了哪些网络层。
3.6.2 学习 Socket 编程
Socket 编程你需要先掌握常用的 Socket API,包括但不局限于:
学习这些 Socket API 的时候,不是让你单纯地记忆这些函数的参数,而是掌握每一个函数的重难点。
例如:
- 如何将一个 socket 设置成非阻塞模式
- 阻塞模式下,send 和 recv 函数行为是什么样子的?非阻塞模式下 send/recv 的返回值分别是什么?
- 客户端发起连接时,如何主动指定通过本地某个端口号去连接?bind 函数如果端口号设置为 0 是什么行为?
- listen 函数的 backlog 参数用途是什么?
- 如何实现异步的 connect 函数?
- accept 函数调用时,三次握手是否已经完成?
- 如何实现半关闭状态?
- nagle 算法的用途是什么?
- select 函数的第一个参数怎么设置?select 函数的超时参数如果设置为 NULL 是什么行为?
需要重点学习下常用的网络模型:
- Windows 上常用的网络模型有 select、WSAEventSelect、WSAAsyncSelect、完成端口模型;
- Linux 上常用的网络模型 select、poll、epoll,epoll 需要重点关注的是水平模式和边缘模式。
当然,也建议一定要理解,不要死记硬背。C++ 的同学来面试的时候,我会给他们准备如下面试题:
- epoll 边缘模式下,某次读取了某个 socket 上的部分数据,下次是否会出发读事件?如果此时又来了一个字节的新数据,是否会触发读事件?
- epoll 边缘模式建议尽量一次把数据读完,怎样判断当前数据已经读完?
- epoll 边缘模式下,对于写事件应该如何处理?
接着还要熟悉 TCP 协议的流式特性,如何解决粘包问题;还要掌握常见的网络协议格式,像 HTTP、FTP、POP3/SMTP/WebSocket协议的格式都建议熟练掌握。
以 HTTP 协议为例,HTTP 协议包的格式是什么样的,包头和包体如何分界的,GET 与 POST 请求的数据分别放在 HTTP 包的什么位置,如果放在包体中,如何知道包体的数据有多长。
3.6.3 学习常用网络命令
学习了常用的网络命令,可以用来排查网络故障与定位问题,反过来,也可以加深对网络理论知识的理解,建议掌握以下命令:ifconfig、ping、telnet、netstat、lsof、nc、curl、tcpdump。
掌握了这些命令要做到学以致用,例如现在某个服务器连接不上,如何使用这些命令判断是自己网络的问题还是目标主机的问题;开发了一个服务器程序,手头上没有可用的客户端,如何使用 nc 命令模拟一个;或者反过来,开发了一个客户端程序,如果用 nc 模拟一个服务器端用于测试。
推荐《TCP/IP 网络编程》和《Linux 高性能服务器编程》:
链接:https://pan.baidu.com/s/1CJhJWxp5FxNNmdcG3rjscQ 提取码:4jlv
3.7 学习数据库
数据库需要掌握的基础知识有:
- 熟悉基本 SQL 操作 包括增删改查(insert、delete、update、select语句),排序 order,条件查询(where 子语句),限制查询结果数量(LIMIT语句)等
- 稍微高级一点的 SQL 操作(如 Group by,in,join,left join,多表联合查询,别名的使用,select 子语句等)
- 索引的概念、索引的原理、索引的创建技巧
- 数据库本身的操作,建库建表,数据的导入导出
- 数据库用户权限控制(权限机制)
- MySQL 的两种数据库引擎的区别
- SQL 优化技巧
以上属于对开发的基本的数据库知识要求,你可以找一本相关入门级的数据库图书,我推荐一下《《MySQL技术内幕(第5版)》。
高级开发除了以上要求还要熟悉高可用 MySQL、主从同步、读写分离、分表分库等技术,这些技术的细节一定要清楚,它们是你成为技术专家或者高级架构的必备知识。我们在实际面试时,在讨论高可用服务服务方案时,很多面试者也会和我们讨论到这些技术,但是不少面试者只知道这些技术的大致思想,细节往往说不清楚,细节不会就意味着你的高可用方案无法落地,企业需要可以落地的方案。
这些技术我首推《高性能 MySQL》这本书,这本书高级开发者一定要通读的,另外还有 2 本非常好的图书也推荐一下:一本是《MySQL 排错指南》,读完这本书以后,你会对整个“数据库世界”充满了清晰的认识;另外一本是《数据库索引设计与优化》,这本书读起来非常舒服,尤其是对于喜欢算法和数据结构的同学来说。
链接:https://pan.baidu.com/s/14AXUkEv4BTqSrdsTq4qGrA
提取码:6xgg
3.8 学习汇编
如果你熟练掌握汇编,你就比其他人多很多优势,你会能透彻地知道你写的每一行 C/C++ 代码背后的机器指令的效率。无论是做安全工程还是自己技术提升上都是非常不错的。这里推荐一本王爽老师的《汇编语言(第 3 版)》,这本书不厚,语言通俗易懂,你也不用刻意去记忆,基本上当小说书看一下就能很快看完了。汇编实战类图书还有另外一本《老码识途:从机器码到框架的系统观逆向修炼之路》。我个人是非常喜欢这本书的。当年读这本书的时候,真的有一种“笑看妻子愁何在?漫卷诗书喜欲狂”的感觉。
链接:https://pan.baidu.com/s/1H8HzcYKcEFog6chHAy_HAQ
提取码:sw3t
链接:https://pan.baidu.com/s/13OL91anyZ34iOYCicGi_Ow
提取码:9abf
3.9 学习代码规范,培养良好代码风格
在你学习的过程中,请一定要认真对待自己每一个变量名、函数名,养成良好的代码习惯。我学生时代花了大量时间去学习一些教人写出优美风格的代码书籍、资料、源码,在你还是个小白的时候,要认真精读一些优秀代码,不仅要学习它们的整体设计思路,还要学习它们的代码风格和细节。这里推荐《程序设计实践》《代码整洁之道》这两本书,特别是《程序设计实践》,强烈建议学生朋友看一下,能大幅度地提高你实际编码的技巧和编码风格。
链接:https://pan.baidu.com/s/1bhXQvnoK67zCBRj2lFSgyA
提取码:e5hs
链接:https://pan.baidu.com/s/1tXoDzBTUjd_kefaK4oNGJg
提取码:kr26
高级部分
接下来是高级部分,高级部分已经与具体的编程语言无关了,这些技术需要长期的专研和经验积累。
高可用与容灾容错
服务都是人开发的,既然是人开发的,必然有宕机的可能性,当然宕机的原因可能是程序自身 bug,也可能是物理故障(断电、磁盘损坏等等),作为开发人员,针对不同的业务场景,我们没法做到服务 100% 可能,至少让其尽量可用,八仙过海各显神通。在宕机时如何尽量不影响业务、如何尽量快速恢复、如何保证数据和业务状态不丢失等等。这当然有一定的固定套路,例如主从、主备,当然固定的套路不是万能的,尤其是对于一些有状态要求的服务,这需要不断的磨练与自我总结。
分布式
分布式你需要掌握基本的分布式理论和原理,常见的分布式算法,然后是分布式系统设计的初衷和技巧,在实际并发量高的业务中,如何利用分布式解决高可用和访问效率问题。
RPC
很多人都听说过这个词,在面试时也可以说出来个大概,但是当问到 RPC 技术解决的核心问题是什么就说不清楚了。当然,学习 RPC,我们还要考虑协议的设计(协议格式、序列化与反序列化、兼容性问题)、网络连接的重试与反馈、接口 stub 的设计等等。
消息中间件
目前除了自己公司自研的消息中间件,主流的有 Kafka、RabbitMQ、RocketMQ,如果想学习,建议选择其中一种深入学习一下,要掌握消息中间件的用途、选举策略、保序策略、重试策略、高可用策略等。
缓存
缓存的设计是一个很大的方面,个人觉得与其说这是一种设计思想而非单纯的某个缓存服务。当然,老生常谈的有缓存雪崩、缓存穿透、缓存击穿的解决思路。当然,以缓存为代表的服务是 Redis,Redis 的常用数据类型、适用场景、持久化、主从复制、哨兵与集群,这些建议你掌握,如果你从来没机会吃猪肉,那就看看猪跑吧,一些技术书籍和项目案例都有 Redis 的用途说明。
数据库高级知识
包括 SQL 调优、数据库调优、分表分库、主从同步等等。
成为研发专家不是一朝一夕的事情,既要相关工作经验的积累,也需要个人勤奋的努力和不断总结,才能达到融会贯通阶段。
C++ 企业级项目
电驴、金山卫士、flamingoIM、filezilla等几套开源源码送给大家大型 C++ 游戏源码
大型仿英雄联盟优质游戏源码,限时领取书单获取
上文中介绍的书单获取方式(含下载方式,书不多,都是经典),可以点这里:
计算机必看经典书籍(含下载方式)一些对 C++ 开发者有用的资源
如何求职 C++ 后端开发岗位轻松搞定技术面试中常见的网络通信问题C++高性能服务器网络框架设计的细节写了这么多文字,老婆催我吃饭去了,如果觉得有帮助,请给 @张小方 点个赞呗~
====================
====================
2024 年 2 月 21 日更新:
我目前在一家外企做C++架构,从业十多年,在编程之路上一路打怪升级走过来,深知个中艰辛。对于工作年长不长,尤其是五年以下的开发者,一味地去追求新技术新框架,最后难免成了只会CRUD的调包侠。我的建议是先深入学好一门重型编程语言、学好操作系统原理(包括多线程编程)、学好计算机网络和网络编程等,这是都是工作早期应该去夯实的必备基本功。
我根据我自己的工作经验和经历写过了相关的技术专栏,以下是专栏目录截图:
我将这些专栏整理成了高清pdf,如果你对这些专栏有兴趣,可以通过下面的链接获取:
CppGuide公众号 技术专栏打包下载原创不易,觉得有用,请给 @张小方 点个赞吧~
如果你在 C++ 学习或者 C++ 求职过程中有什么问题,也可以向小方提问(不需要特别付费,一杯奶茶钱而已):