一、我的编程经
说说我的编程经历,在C++和Java之间我经历了几个阶段:
- 大学期间,我浅尝辄止地学习了一段时间的Java,但后来放弃了,开始学习C/C++。
- 本科毕业后,我选择攻读硕士学位,并一直专注于C++的学习。
- 工作后,我一直从事C++开发工作,先是从事C++客户端开发,后来转向了C++服务器开发。
二、选择C++还是选择Java?
作为一位经验丰富的人,我想给出以下建议: 如果你是学生或者有大量空闲时间,我建议你将C++学好。C++被称为程序员的"九阳神功"并非毫无根据,这并不是因为C++有多难学,而是因为学习C++的技术栈涵盖了一系列操作系统原理。如果你能够掌握C++,就意味着你掌握了这些背后的原理。之后,学习其他任何语言和机制都会更轻松。
然而,如果你急需找工作,或者对编程并没有太大的兴趣,只是为了谋生,那么你可以优先选择Java,甚至可以只掌握Java的一些基本概念和常见的面试题。如果运气好的话,也有可能找到一份不错的工作。
三、如何学习C++?
学习C/C++这门语言与其他高级语言不同,它更靠近操作系统的底层。因此,为了学好C/C++技术栈,必须结合操作系统的运行机制进行学习。简单来说,你需要掌握几个基础知识:汇编语言、编译链接和运行时体系、操作系统原理、多线程编程以及网络编程。只有通过这样的学习,你才能真正理解、掌握并将所学应用于实践。我们学习C++不是为了纯粹的理论研究,而是要将所学投入实际生产中去。
下面是一张图来概括C++技术栈的内容:
3.1 学习C++语言的内容包括常用的C++11/14/17语法。
在C++的面试中,通常会问到以下几个与语法相关的问题,这些问题也是C++开发所必备的知识:
- 在具有继承关系的父子类中,构造和析构子类对象时,父子构造函数和析构函数的执行顺序是怎样的?
- 在具有继承关系的类体系中,父类的构造函数和析构函数一定要声明为虚函数吗?如果不声明为虚函数会有什么影响?
- 什么是C++多态?C++多态的实现原理是什么?
- 什么是虚函数?虚函数的实现原理是什么?
- 什么是虚表?虚表的内存结构布局是怎样的?虚表的第一项(或第二项)是什么?
- 在菱形继承(类D同时继承类B和类C,而B和C又继承自类A)体系下,虚表在各个类中的布局是怎样的?如果类B和类C同时拥有一个成员变量m,m在D对象的内存地址上的分布情况如何?是否会相互覆盖? 以上是与C++语法相关的问题,深入理解这些知识点将有助于学好C++语言。尽量通过阅读相关资料和教材来更详细地了解这些概念和原理。
- 统一的类成员初始化语法与 std::initializer_list<T>
- 注解标签(attributes)
- final/override/=default/=delete 语法
- auto 关键字
- Range-based 循环语法
- 结构化绑定stl
- 容器新增的实用方法
- std::thread
- 线程局部存储
- thread_local
- 线程同步原语 std::mutex、std::condition_variable 等
- 原子操作类
- 智能指针
- std::bind/std::function
3.2 提升C++水平
一旦你掌握了C++语言本身,你可以进一步学习一些常见的C++惯用法和高性能编码实践。具体来说,可以深入学习临时对象的使用、内存管理技巧、继承的最佳实践、虚函数的运用、内联函数的优化、引用计数等与提升C++效率相关的细节内容。此外,还可以学习STL(标准模板库)的使用,它是C++中非常强大和常用的工具库。通过学习这些内容,可以进一步提升你的C++编程水平,使你能够编写高效、可靠的C++代码。
3.3 C++ 工程实践
在掌握了 C++ 常用语法和语言背后的实现机制和常用惯用法后,如何在实际开发中如何设计 C++ API 接口,大型 C++ 程序小到单个 .h/cpp 文件如何编写,大到大型 C++ 项目如何组织,这些知识点你需要了解。
3.4 与C/C++相关的必备知识 - 汇编
了解汇编是与C/C++相关的基础知识之一。学习汇编并不意味着一定要用汇编来编写代码,就像学习C/C++并不仅仅是为了面试和找工作一样。
对于C/C++的学习者来说,掌握汇编是强烈建议的,因为只有这样,你才能清楚地了解每一行C++代码背后所对应的机器指令,了解基本的程序结构如if/for/while是如何实现的,了解函数的返回值是如何返回的,以及为什么整型变量的数学运算不是原子操作。
通过掌握汇编,你可以明确地知道在C++中,栈对象从构造到析构的整个生命周期中,开发者的代码、编译器和操作系统各自承担了哪些任务。掌握汇编后,你可以理解函数调用的实现原理,了解不同的函数调用方式,以及为什么像printf这样的函数必须采用__cdecl的方式而不能是__stdcall。此外,通过掌握汇编,你还可以理解为什么添加一个类的成员函数不会增加其实际占用的内存空间。掌握了汇编,你将能够更加高效地编写C++代码。
第二个基础知识 - 编译、链接与运行时体系
作为开发者,了解编译、链接和运行时体系知识是非常重要的。我们需要清楚地了解我们编写的C/C++程序是如何经过预处理、编译和链接等步骤最终转变为可执行的二进制文件的。还需要了解操作系统如何识别一个文件为可执行文件,以及可执行文件包含哪些内容。在程序执行时,需要知道如何加载到进程的地址空间中,以及程序的每个变量和数据在进程地址空间的哪个位置,并且如何引用它们。此外,还需要了解一个进程的地址空间包含哪些内容,各个地址段分布了什么内容,以及为什么读写空指针或野指针会出现问题。对于编译、链接和运行时体系的了解,有助于我们更好地理解代码的执行过程和内存管理,以避免潜在的错误和优化程序性能。
第三个基础知识 - 狭义的操作系统原理
狭义的操作系统原理是指操作系统在特定范畴内的原理和机制。在广义的操作系统原理中,我们已经涵盖了之前提到的内容。而狭义的操作系统原理则包括操作系统如何管理进程和线程,以及虚拟内存与物理内存之间的对应关系。此外,还包括了内存映射文件的概念和实现方式,以及进程之间的通信机制等等。了解狭义的操作系统原理可以帮助我们深入理解操作系统的内部工作原理,从而更好地进行程序开发和系统优化。
第四个基础知识-多线程
尽管多线程知识在第三点中已经提到了,但我单独列出来是因为多线程编程是应用服务中常用的技术之一。最近我面试了几个学历非常好的同学,发现他们对于一个进程中如果某个线程因为内存问题退出是否会导致整个进程退出的问题,回答得不理想。这是不应该的。其实,多线程知识并不难学习,只要真正理解并实践,而非为了应付面试,就能学得很好。无论是Windows还是Linux操作系统,提供的线程同步对象都有几种基本类型。在Windows中,常见的有临界区(关键区)、事件、互斥体、信号量等;而在Linux中,有互斥体、信号量、读写锁、条件变量等。这些知识点只要花几天时间就能弄清楚。
大多数同学之所以不会,不是因为学不会,而是不愿意学。然而,他们却喜欢在简历上写熟悉多线程编程。面试时,如果被问到条件变量的虚假唤醒机制,他们却说不清楚,却非要说自己用过条件变量。这是一些同学犯的低级错误。如果真的用过条件变量,却不了解虚假唤醒机制,那么他们编写的代码一定是有问题的。
掌握了常见的多线程同步原语后,接下来可以找一些包含多线程的项目来学习,无论是否带有UI。我推荐一种方式,使用gdb或者Visual Studio调试器,将你想要学习的多线程程序进行断点调试,在多线程面板中查看该进程中有多少个正在运行的线程,分析每个线程的作用,然后研究这些线程是何时何地创建的,以及为什么需要创建新的线程。尝试过几个多线程项目后,你将对多线程编程变得更熟练。
第五个基础知识-网络编程
简单地说就是 Socket 编程。操作系统提供的网络编程接口在相当长的时间内保持不变,一旦掌握,将受益终生。理解和掌握基本的 Socket API 不仅可以自定义各种网络通信框架,还可以轻松使用市面上流行的网络通信库。更重要的是,它将成为你解决各种网络问题的坚实技术支持。虽然像 Java、Go、Python 等语言对网络编程进行了封装,但作为技术的源头,我们有什么理由不去掌握它呢?
总而言之,学习 C++ 不仅仅是学习语法本身,还需要熟悉与 C++ 技术栈相关的操作系统原理。
最后,老舅整理一个完整的学习路线,适用于从零开始学习 C/C++ Linux 后端服务器开发(参考腾讯 T8 技术栈)