个人整理的C++大概学习路线

关键词:C++


by:Fingsinz,Reference:C++ Developer Roadmap

C++ 语言介绍

什么是C++

  • C++作为C编程语言的扩展,提供了类和继承等面向对象的特性。

  • C++被广泛应用于游戏开发、系统编程、嵌入式系统和高性能计算等各种应用中。

  • C++是一种静态类型语言,这意味着变量的类型是在编译过程中确定的,并且C++有一个称为C++标准库的扩展库,它为各种任务提供了一组丰富的函数、算法和数据结构。

为什么使用C++

  • 高性能:C++旨在提供高性能和高效率。它提供了对系统资源的细粒度控制,使优化软件变得更容易。

  • 可移植性:不同的计算机体系结构和操作系统都支持C++,允许您编写在各种平台上运行的可移植代码,而无需进行重大修改。

  • 面向对象编程:C++支持面向对象编程(OOP)——一种允许您使用类和对象设计程序的范例,从而实现更好的代码组织和可重用性。

  • 支持低级和高级编程:C++允许您编写低级代码,如内存操作,以及高级抽象,如创建类和使用标准模板库(STL)。

  • 丰富的库支持:C++提供了大量的库和工具,如标准模板库(STL)、Boost和Qt等,它们可以帮助您开发项目并使其更高效。

  • 兼容C语言:C++可以与C结合使用,提供两种语言的功能,并允许您重用现有的C代码。通过合并C++特性,您可以增强代码并改进其功能。

  • 活跃的社区支持:C++已经存在了很长一段时间,并且拥有一个庞大而活跃的用户社区,他们为语言的发展做出了贡献,表达了新的想法,并参与了有助于语言进一步发展的讨论。这让你更容易找到解决问题的方法。

C和C++的区别

  • 语法语义区别、代码可重用性和模块化、错误处理

C++版本

  • C++0x:指的是C++ 11的工作名称,在其最终发布之前,它以前被称为C++ 0x。C++ 11是2011年发布的C++语言标准的主要修订版,它为该语言带来了几个新特性和改进。

    • auto、基于范围的循环:for(int i : array)、匿名Lambda函数、nullptr、右值引用和移动语义、可变模板、静态判断assert、支持线程
  • C++14:指的是2014年发布的C++版本。

    • 范式Lambda、decltype关键字、可变模板
  • C++17:也称为C++ 1z,是2017年12月发布的C++编程语言版本。

    • if中的初始化:if(auto x = map.find(key); x != map.end())、结构化绑定声明、inline变量、折叠表达式、constexpr if 语句、改进Lambda表达式、标准文件系统库、string_view标准库、对标准库算法支持并行运算
  • C++20:C++ 20是2020年发布的C++版本。

    • concept关键字;基于范围的处理序列;协同程序;constexprconsteval关键字:带有constexpr标记的函数可以在编译时或运行时执行,而带有consteval标记的函数只能在编译时执行;最新C++

配置C++开发环境

安装C++

  • 在开始用C++编程之前,您需要在系统上安装一个编译器。编译器是一个将你写的C++代码转换成计算机可以运行的可执行文件的程序。

编译器

  • 编译器是一种计算机程序,它将用一种编程语言编写的源代码翻译成另一种语言,通常是机器代码或汇编代码,可以由计算机处理器直接执行。在c++环境中,编译器将您编写的c++源代码转换为可执行程序。

  • 常见编译器

    • GNU Compiler Collection(GCC)、Clang、Microsoft Visual C++(MSVC)、Intel C++ Compiler(ICC)
  • 编译器状态:C++的编译过程可以分为四个主要阶段:预处理、编译、汇编和链接。每个阶段执行一个特定的任务,最终将源代码转换为可执行程序。

    • 预处理(Preprocessing):第一个阶段是源代码的预处理。预处理器在实际编译过程之前修改源代码。在这个阶段,将展开包含的头文件,替换宏,并处理条件编译语句。

    • 编译(Compilation):第二阶段是实际编译经过预处理的源代码。编译器将修改后的源代码转换为中间表示形式,通常特定于目标处理器体系结构。此步骤还包括执行语法检查、语义分析,并为源代码中遇到的任何问题生成错误消息。

    • 汇编(Assembly):第三阶段是将编译器的中间表示转换成汇编语言。此阶段使用特定于目标处理器体系结构的助记符和语法生成汇编代码。然后,汇编程序将该汇编代码转换为目标代码(机器代码)。

    • 链接(Link):第四阶段是将目标代码与必要的库和其他目标文件链接起来。在此阶段,链接器合并多个目标文件和库,解析来自其他模块或库的外部引用,为函数和变量分配内存地址,并生成可在目标平台上运行的可执行文件。

编辑器、集成开发环境(IDE)

  • 代码编辑器是专门为编辑、管理和编写源代码而设计的程序。它们提供了广泛的功能,使开发过程更容易和更快。

  • Visual Studio、Visual Studio Code、CLion、Sublime Text、CodeBlock

运行第一个C++程序

  • Hello_World.cpp

基础语法

基础操作

  • 算术运算符:+-*/%++--

  • 逻辑运算符:and&&)、or||)、not!

  • 位运算符:&|^~<<>>

  • 条件:if/switch

  • 循环:for/while

函数

  • 函数声明:仅有返回类型、函数名称和参数。

  • 函数定义:具有详细的函数体。

  • 函数原型:函数原型是没有函数体的函数声明,它告诉编译器函数的名称、返回类型和参数。

  • 普通函数:函数是执行特定任务的一组语句,在程序中组织为一个单独的单元。

    • 标准库函数、自定义函数
  • 运算符函数

    • C++中的运算符是对数据执行各种操作的符号,如算术、比较和逻辑操作。它们用于操作和计算表达式和变量。

    • 赋值运算符;逻辑运算符;关系运算符;算术运算符

  • Lamda匿名函数

数据类型

  • 静态数据类型

    • 意味着变量的数据类型是在编译时,在程序执行之前确定的。
  • 动态数据类型

    • 意味着在运行时确定变量的数据类型。

    • void*指针:void*指针是一种泛型指针,可以指向任何数据类型的对象。它们可以用于存储对任何类型对象的引用,而无需知道对象的特定类型。

    • std::any(C++17):std::any类,它代表了任何类型的单个值的广义类型安全容器。

    • void*指针和std::any都有性能影响,因为在运行时进行了额外的类型检查和类型转换。它们应该谨慎使用,只有在绝对必要的时候才使用。

  • 运行时类型标识RTTI

    • 允许在程序执行期间获取对象的类型信息。这在使用动态类型时非常有用,因为对象的类型可以在运行时更改。

    • typeid操作符、dynamic_cast操作符

    • 使用RTTI可能会有一些性能开销,因为它需要在运行时存储和处理编译器生成的额外信息。

指针和引用

  • 普通指针:字符指针、整型指针、浮点型指针、结构体指针、函数指针……

  • 原始指针

    • 原始指针是直接保存内存地址的低级结构。它们可以用于手动分配内存、创建动态数组和有效地传递值等。

    • new/delete操作符

    • 内存泄漏

  • 智能指针

    • unique_ptrshared_ptrweak_ptr
  • 内存模型

    • 栈内存、堆内存、数据段、代码段
  • 对象生存期

    • 静态存储持续期、线程存储持续期、自动存储持续期、动态存储持续期
  • 引用

结构体和类

  • 面向对象编程:类、封装、继承多态

  • 静态多态

    • 静态多态性,也称为编译时多态性,是一种在编译时而不是在运行时解析类型和方法调用的多态性。这通常是通过使用C++中的函数重载和模板来实现的。

    • 函数重载:一种创建具有相同名称但不同参数列表的多个函数的方法。编译器根据调用函数时使用的参数类型和数量确定要调用的正确函数。

    • 模板:允许创建泛型函数或类。特定类型的实际代码是在编译时生成的,这避免了运行时多态的开销。模板的使用是c++中实现静态多态性的主要技术。

  • 动态多态

    • 动态多态性是通过虚函数实现的,虚函数是用virtual关键字标记的基类的成员函数。在基类中指定虚函数时,可以在任何派生类中重写虚函数,以提供不同的实现。

    • 虚函数:在基类的方法声明中使用virtual关键字。这告诉编译器该方法应该被视为虚方法,允许它被派生类覆盖。

    • 虚表:虚表(或Vtable)是C++编译器用来支持动态多态性的一种机制。在动态多态性中,根据实际的对象类型,在运行时调用适当的函数。

  • 多继承:一个类可以从多个父类继承特征(数据成员和成员函数),一个类可以有多个基类。

  • 菱形继承:一个类派生自两个或多个类,而这些类又派生自一个公共基类

构建代码库

  • 代码分割、代码风格、头文件和Cpp文件、头文件警卫、命名空间

  • 作用域

    • 全局作用域、局部作用域、名称空间作用域、类作用域
  • 前向声明:类前向声明、函数前向声明、枚举和typedef前向声明

语言概念

习惯术语

  • auto关键字:用于自动类型推导。

  • 类型转换:static_castconst_castdynamic_castreinterpret_cast

  • 未定义行为:指由于违反语言规则而无法预测程序行为的情况。

  • 参数依赖查找、命名修饰、宏定义、定义并初始化(资源获取即初始化RAII)、指针到实现(Pimpl)、奇异递归模板模式(CRTP)、不可复制/不可移动、擦除、复制和交换、惰性复制

调试器

  • 理解调试信息:错误、警告、通知

  • 调试器符号:内部调试符号、外部调试符号。

  • WinDbg:一个功能强大的Windows应用程序调试器。

  • GDB:GNU Project Debugger,一个功能强大的命令行调试器。

构建系统

  • Cmake、Makefile、Ninja

异常处理

异常

  • try关键字:定义应该监视异常的代码块

  • catch关键字:指定要捕获的异常类型以及异常发生时应执行的代码块。

  • throw关键字:抛出一个异常,该异常将被捕获并由适当的捕获块处理

  • noexcept关键字:指定一个函数,该函数不会抛出异常,也不会在其作用域中抛出异常时终止程序。

标准异常

  • std::exception:所有标准异常的基类。

  • std::logic_error:表示程序可以静态检测到的错误。

  • std::runtime_error:表示程序执行期间发生的错误。

退出代码

  • 退出码,也称为“返回码”或“状态码”,是程序完成执行后返回给调用环境(通常是操作系统)的数值。这些代码用于指示程序执行的成功或失败。

  • 0是成功执行的标准退出代码,而非零退出代码通常表示错误或其他异常情况。非零退出码的实际含义在不同的应用程序或系统之间可能有所不同。

  • 在C++中,您可以使用return语句从main函数返回退出代码,也可以使用exit()函数,它是C++标准库的一部分。

访问冲突

  • 访问冲突是程序试图访问非法内存位置时发生的一种特定类型的错误。

  • 可能引起原因:取消引用空指针或无效指针;访问数组越界;对由用户或操作系统释放的内存进行阅读或写。

标准模板库STL

标准输入输出流

  • iostream是C++标准库中的一个头文件,它提供了基本输入和输出(I/O)操作的功能。I/O流促进了程序与各种源(如控制台、文件或其他程序)之间的通信。

处理日期和时间

  • 使用chrono库来处理日期和时间,它是标准库(Standard library, STL)的一部分。chrono库提供了各种数据类型和函数来表示和操作时间持续时间、时间点和时钟。

算法

  • C++中的标准模板库(Standard Template Library, STL)提供了一组泛型算法,用于处理各种容器类。这些算法是作为函数实现的,可以应用于不同的数据结构,如数组、向量、列表等。算法的主要头文件是<algorithm>

迭代器

  • 迭代器是C++标准库(STL)中的对象,帮助我们遍历数组、列表和向量等容器。本质上,它们充当容器类和算法之间的桥梁。迭代器的行为类似于指针,但提供了一种更通用和抽象的方式来访问容器中的元素。

  • 输入迭代器、输出迭代器、前向迭代器、反向迭代器、双向迭代器、随机访问迭代器

容器

  • Pair对、Vector向量、List列表、String字符串、Stack栈、Queue队列、Deque双向队列、Priority_queue优先队列、Set集合(unordered_set、multiset、unordered_multiset)、Map映射(unordered_map、multimap、unordered_multimap)、Bitset压位

多线程

  • 多线程是指在单个进程或程序中并发执行多个线程。它允许并行执行多个任务,从而提高了应用程序的性能和效率。

  • 线程创建、带参数线程、互斥锁和锁

模板编程

可变模板

  • 可变模板是C++ 11中的一个特性,它允许定义具有可变数量参数的模板。当您需要编写一个可以接受不同数量和类型参数的函数或类时,这尤其有用。

模板专门化

  • 全模板专门化:完全模板专门化允许在与特定类型参数集一起使用时为模板提供特定的实现或行为。

  • 部分模板专门化:允许为可能的类型参数的子集专门化模板。当希望为特定类型组提供自定义实现,而不必为该组中的所有类型定义单独的专门化时,它特别有用。

类型特征

  • 类型特征是C++中的一组模板类,可以帮助获取有关类型属性、行为或特征的信息。它们可以在<type_traits>头文件中找到。通过使用Type Traits,可以根据给定类型的属性来调整代码,甚至可以在模板代码中为类型参数强制执行特定的属性。

替换失败不是错误(SFINAE)

  • SFINAE是C++模板元编程中的一个原则,它允许编译器在替换过程中特定模板特化失败时选择适当的函数或类。术语“替换失败”是指编译器试图将模板参数替换为函数模板或类模板的过程。如果替换导致错误,编译器不会将特定的专门化视为候选专门化,而是继续搜索有效的专门化。

  • SFINAE背后的关键思想是,如果发生替换错误,它将被静默地忽略,编译器将继续探索其他模板专门化或重载。这允许编写更灵活和通用的代码,因为它使能够针对不同的场景拥有多个专门化。

包管理

  • 包管理器是自动化安装、升级和管理编程语言(如C++)软件(库、框架和其他依赖项)过程的工具

  • vcpkg、Spack、Conan、NuGet

库和框架编程

  • 在使用C++时,可能需要使用外部库来协助完成各种任务。库是预编译的代码片段,可以在程序中重用,以执行特定任务或提供特定功能。在C++中,库可以是静态库(.lib)或动态库(Windows中的.dll, Unix/Linux中的.so)。

  • 静态库:在编译时被合并到程序中。它们与代码链接,创建更大的可执行文件,但在运行时不需要任何外部文件。

  • 动态库:动态库是在运行时加载的,这意味着可执行文件只包含对这些库的引用。这些库需要在运行程序的系统上可用。

C++库

  • OpenCV、POCO、protobuf、gRPC、Tensorflow、pybind11、spdlog、OpenCL、ranges_v3、fmt、Boost

框架

  • gtest/gmock、Qt、Catch2、Orbit Profiler、PyTorch C++