0%

C++

语法树

C++ 语法概览

基础语法特性
C++98/03

基本数据类型

整型: int, short, long

浮点型: float, double

字符型: char, wchar_t

布尔型: bool

修饰符: signed, unsigned

变量与常量

变量声明与定义

常量: const

运算符

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

关系运算符: ==, !=, <, >

逻辑运算符: &&, ||, !

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

赋值运算符: =, +=, -=

其他: sizeof, typeid

控制结构

条件语句: if, else

开关语句: switch, case

循环: for, while, do-while

跳转: break, continue, return, goto

函数

函数声明与定义

参数传递: 值, 引用

默认参数

函数重载

内联函数: inline

指针与引用

指针: *

引用: &

空指针: NULL

类与对象

类定义: class, struct

访问控制: public, private

构造函数与析构函数

拷贝构造函数

静态成员: static

友元: friend

继承

多态性: virtual, =0

模板

函数模板

类模板

异常处理

try, catch, throw

标准异常类

命名空间

定义与使用

动态内存管理

new, delete

预处理器

宏定义: #define

条件编译: #ifdef

文件包含: #include

现代 C++ 新特性

C++11

自动类型推导: auto, decltype

范围 for 循环

nullptr

智能指针: unique_ptr, shared_ptr

移动语义: &&, std::move

完美转发: std::forward

Lambda 表达式

模板改进: 可变参数模板

初始化改进: {}

并发支持: std::thread

新容器: array, unordered_map

constexpr, static_assert

C++14

泛型 Lambda

返回类型推导: auto

constexpr 扩展

变量模板

C++17

结构化绑定

if/switch 初始化

折叠表达式

std::optional, variant, any

文件系统库: std::filesystem

并行算法

C++20

概念: Concepts

Ranges 库

协程: co_await, co_yield

模块: import

三路比较: <=>

consteval, constinit

std::span, bit_cast

其他特性

标准库扩展
C++98+

STL: vector, map

输入输出: iostream

字符串: string, string_view

正则表达式: regex

编译器特性
C++11+

属性: [[nodiscard]]

对齐控制: alignas

详解

1. 基础语法特性(C++98/03及之前)

基本数据类型

C++ 的基本数据类型是语言的核心,用于定义变量以存储不同种类的数据。这些类型的具体大小和范围依赖于编译器和硬件平台,但 C++ 标准提供了一些基本保证。以下是对每种数据类型的详细讲解。

整型

整型用于存储整数值,根据大小和符号性分为以下几种:

  • int

    • 功能:表示基本的整数类型,通常是平台上最自然的大小(一般 4 字节,32 位)。
    • 范围:有符号时,范围通常为 -2,147,483,6482,147,483,647-2^312^31-1)。
    • 使用场景:适用于大多数整数计算,如循环计数器、数组索引等。
    • 底层原理:存储为二进制补码形式,符号位决定正负。
    • 注意事项:溢出时行为未定义(例如 INT_MAX + 1)。
    • 示例代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      #include <iostream>
      #include <limits>
      int main() {
      int i = 42; // 普通整数
      std::cout << "int value: " << i << "\n";
      std::cout << "int min: " << std::numeric_limits<int>::min() << "\n"; // -2147483648
      std::cout << "int max: " << std::numeric_limits<int>::max() << "\n"; // 2147483647
      i = INT_MAX; // 使用 limits 中的宏定义
      std::cout << "Max int: " << i << "\n";
      // i = i + 1; // 未定义行为,溢出
      return 0;
      }
  • short

    • 功能:短整型,比 int 小,通常 2 字节(16 位)。
    • 范围:有符号时,范围为 -32,76832,767-2^152^15-1)。
    • 使用场景:适合存储较小的整数,节省内存,如小型计数器。
    • 底层原理:同样使用二进制补码表示。
    • 注意事项:范围较小,需注意溢出。
    • 示例代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #include <iostream>
      #include <limits>
      int main() {
      short s = 32767; // 短整型最大值
      std::cout << "short value: " << s << "\n";
      std::cout << "short min: " << std::numeric_limits<short>::min() << "\n"; // -32768
      std::cout << "short max: " << std::numeric_limits<short>::max() << "\n"; // 32767
      // s = 32768; // 溢出,未定义行为
      return 0;
      }
  • long

    • 功能:长整型,至少与 int 大小相同,通常 4 字节或 8 字节(视平台)。
    • 范围:32 位系统上有符号时为 -2^312^31-1,64 位系统上可能更大。
    • 使用场景:需要更大范围的整数,如文件大小。
    • 底层原理:补码表示,长度由编译器定义。
    • 注意事项:建议使用 long long 以确保 64 位支持。
    • 示例代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #include <iostream>
      #include <limits>
      int main() {
      long l = 123456L; // 长整型,带 L 后缀
      std::cout << "long value: " << l << "\n";
      std::cout << "long min: " << std::numeric_limits<long>::min() << "\n";
      std::cout << "long max: " << std::numeric_limits<long>::max() << "\n";
      return 0;
      }
  • long long

    • 功能:超长整型,至少 8 字节(64 位),C++11 正式标准化。
    • 范围:有符号时为 -2^632^63-1(约 ±9.2×10^18)。
    • 使用场景:非常大的整数,如科学计算或时间戳。
    • 底层原理:补码表示,保证 64 位。
    • 注意事项:较老的编译器可能不支持。
    • 示例代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #include <iostream>
      #include <limits>
      int main() {
      long long ll = 123456789LL; // 超长整型,带 LL 后缀
      std::cout << "long long value: " << ll << "\n";
      std::cout << "long long min: " << std::numeric_limits<long long>::min() << "\n";
      std::cout << "long long max: " << std::numeric_limits<long long>::max() << "\n";
      return 0;
      }
  • unsigned 修饰符

    • 功能:将整型变为无符号,范围从 0 开始,最大值翻倍。
    • 范围:如 unsigned int 为 0 到 4,294,967,295(2^32-1)。
    • 使用场景:非负数场景,如计数器、数组大小。
    • 底层原理:直接存储二进制值,无符号位。
    • 注意事项:与有符号类型混合运算需小心(如比较时可能出错)。
    • 示例代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #include <iostream>
      #include <limits>
      int main() {
      unsigned int ui = 4294967295U; // 无符号整数,带 U 后缀
      std::cout << "unsigned int value: " << ui << "\n";
      std::cout << "unsigned int min: " << std::numeric_limits<unsigned int>::min() << "\n"; // 0
      std::cout << "unsigned int max: " << std::numeric_limits<unsigned int>::max() << "\n"; // 4294967295
      return 0;
      }

浮点型

浮点型用于存储小数,基于 IEEE 754 标准。

  • float

    • 功能:单精度浮点数,4 字节,约 7 位有效数字。
    • 范围:大约 ±3.4×10^38,精度有限。
    • 使用场景:需要小数但精度要求不高的场景,如图形计算。
    • 底层原理:分为符号位、指数和尾数,遵循 IEEE 754。
    • 注意事项:浮点运算可能有精度误差(如 0.1 + 0.2 != 0.3)。
    • 示例代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #include <iostream>
      #include <limits>
      int main() {
      float f = 3.14f; // 单精度,带 f 后缀
      std::cout << "float value: " << f << "\n";
      std::cout << "float min: " << std::numeric_limits<float>::min() << "\n"; // 最小正值
      std::cout << "float max: " << std::numeric_limits<float>::max() << "\n";
      std::cout << "0.1 + 0.2: " << (0.1f + 0.2f) << "\n"; // 0.30000001(精度误差)
      return 0;
      }
  • double

    • 功能:双精度浮点数,8 字节,约 15 位有效数字。
    • 范围:大约 ±1.8×10^308,精度更高。
    • 使用场景:需要较高精度的浮点计算,如科学计算。
    • 底层原理:IEEE 754,双倍尾数位。
    • 注意事项:仍可能有精度误差,但比 float 小。
    • 示例代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #include <iostream>
      #include <limits>
      int main() {
      double d = 3.1415926535; // 双精度
      std::cout << "double value: " << d << "\n";
      std::cout << "double min: " << std::numeric_limits<double>::min() << "\n";
      std::cout << "double max: " << std::numeric_limits<double>::max() << "\n";
      return 0;
      }
  • long double

    • 功能:扩展精度浮点数,大小和精度视平台而定(常 10 或 16 字节)。
    • 范围:通常比 double 大,精度更高。
    • 使用场景:需要极高精度的计算,如数学库。
    • 底层原理:依赖编译器实现,可能非 IEEE 754。
    • 注意事项:移植性差,不同平台行为不同。
    • 示例代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #include <iostream>
      #include <limits>
      int main() {
      long double ld = 3.141592653589793238L; // 扩展精度,带 L 后缀
      std::cout << "long double value: " << ld << "\n";
      std::cout << "long double min: " << std::numeric_limits<long double>::min() << "\n";
      std::cout << "long double max: " << std::numeric_limits<long double>::max() << "\n";
      return 0;
      }

字符型

字符型用于存储单个字符或宽字符。

  • char

    • 功能:存储单字节字符,1 字节(8 位)。
    • 范围:有符号时 -128127,无符号时 0255
    • 使用场景:表示 ASCII 字符,如字母、数字。
    • 底层原理:直接存储字符的编码值(如 ASCII)。
    • 注意事项:默认是否带符号由编译器决定。
    • 示例代码
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      char c = 'A'; // ASCII 值为 65
      std::cout << "char: " << c << "\n";
      std::cout << "ASCII value: " << (int)c << "\n"; // 65
      return 0;
      }
  • wchar_t

    • 功能:宽字符,用于存储多字节字符(如 Unicode),大小视平台(常 2 或 4 字节)。
    • 范围:依赖实现,通常支持更大的字符集。
    • 使用场景:国际化程序中支持非 ASCII 字符。
    • 底层原理:存储 Unicode 或其他宽字符编码。
    • 注意事项:需要宽字符流(如 wcout)输出。
    • 示例代码
      1
      2
      3
      4
      5
      6
      #include <iostream>
      int main() {
      wchar_t wc = L'中'; // 宽字符,带 L 前缀
      std::wcout << "wchar_t: " << wc << "\n";
      return 0;
      }

布尔型

  • bool
    • 功能:表示逻辑值 true(1)或 false(0),通常 1 字节。
    • 使用场景:条件判断、标志位。
    • 底层原理:存储为整数,0 表示假,非 0 表示真。
    • 注意事项:可以隐式转换为整数。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #include <iostream>
      int main() {
      bool b1 = true;
      bool b2 = false;
      std::cout << "true: " << b1 << ", false: " << b2 << "\n"; // 1 0
      int i = b1; // 隐式转换
      std::cout << "Converted to int: " << i << "\n"; // 1
      return 0;
      }

变量与常量

变量是程序中可修改的数据存储单元,常量则是不可修改的固定值。

  • 变量

    • 功能:通过类型声明创建变量,可随时修改其值。
    • 使用场景:存储临时数据、计算中间结果。
    • 底层原理:分配内存空间,变量名映射到地址。
    • 注意事项:未初始化变量的值未定义。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #include <iostream>
      int main() {
      int x; // 声明,未初始化
      x = 10; // 赋值
      std::cout << "x: " << x << "\n"; // 10
      x = 20; // 修改
      std::cout << "Modified x: " << x << "\n"; // 20
      return 0;
      }
  • 常量

    • 功能:使用 const 修饰,值在初始化后不可修改。
    • 使用场景:定义不变的值,如数学常数 PI。
    • 底层原理:编译器确保常量不可写,可能优化为内联值。
    • 注意事项:必须初始化,否则编译错误。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      const int y = 30; // 常量,必须初始化
      std::cout << "y: " << y << "\n"; // 30
      // y = 40; // 错误:常量不可修改
      return 0;
      }

运算符

C++ 提供了丰富的运算符,用于执行算术、逻辑、位操作等。以下逐一详细讲解。

算术运算符

用于基本的数学运算。

  • +(加法)

    • 功能:将两个操作数相加。
    • 使用场景:数值计算。
    • 注意事项:整数溢出未定义,浮点数可能有精度误差。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 5, b = 3;
      int sum = a + b;
      std::cout << "5 + 3 = " << sum << "\n"; // 8
      return 0;
      }
  • -(减法)

    • 功能:从第一个操作数减去第二个。
    • 使用场景:计算差值。
    • 注意事项:同加法,注意溢出。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 5, b = 3;
      int diff = a - b;
      std::cout << "5 - 3 = " << diff << "\n"; // 2
      return 0;
      }
  • *(乘法)

    • 功能:两个操作数相乘。
    • 使用场景:面积、体积计算等。
    • 注意事项:溢出风险更大。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 5, b = 3;
      int prod = a * b;
      std::cout << "5 * 3 = " << prod << "\n"; // 15
      return 0;
      }
  • /(除法)

    • 功能:第一个操作数除以第二个。
    • 使用场景:平均值计算。
    • 注意事项:整数除法结果截断,除以 0 未定义。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #include <iostream>
      int main() {
      int a = 10, b = 3;
      int div = a / b;
      std::cout << "10 / 3 = " << div << "\n"; // 3(截断)
      double d = static_cast<double>(a) / b;
      std::cout << "10.0 / 3 = " << d << "\n"; // 3.33333
      return 0;
      }
  • %(取模)

    • 功能:返回除法后的余数,仅适用于整数。
    • 使用场景:判断奇偶、循环计数。
    • 注意事项:除以 0 未定义。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 10, b = 3;
      int mod = a % b;
      std::cout << "10 % 3 = " << mod << "\n"; // 1
      return 0;
      }

关系运算符

用于比较两个值,返回布尔结果。

  • ==(等于)

    • 功能:检查两个操作数是否相等。
    • 使用场景:条件判断。
    • 注意事项:浮点数比较需注意精度。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 5, b = 5;
      bool eq = (a == b);
      std::cout << "5 == 5: " << eq << "\n"; // 1
      return 0;
      }
  • !=(不等于)

    • 功能:检查两个操作数是否不相等。
    • 使用场景:排除特定值。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 5, b = 3;
      bool neq = (a != b);
      std::cout << "5 != 3: " << neq << "\n"; // 1
      return 0;
      }
  • <(小于)

    • 功能:检查第一个操作数是否小于第二个。
    • 使用场景:排序、循环条件。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 3, b = 5;
      bool lt = (a < b);
      std::cout << "3 < 5: " << lt << "\n"; // 1
      return 0;
      }
  • >(大于)

    • 功能:检查第一个操作数是否大于第二个。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 5, b = 3;
      bool gt = (a > b);
      std::cout << "5 > 3: " << gt << "\n"; // 1
      return 0;
      }
  • <=(小于等于)

    • 功能:检查第一个操作数是否小于或等于第二个。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 5, b = 5;
      bool le = (a <= b);
      std::cout << "5 <= 5: " << le << "\n"; // 1
      return 0;
      }
  • >=(大于等于)

    • 功能:检查第一个操作数是否大于或等于第二个。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 5, b = 3;
      bool ge = (a >= b);
      std::cout << "5 >= 3: " << ge << "\n"; // 1
      return 0;
      }

逻辑运算符

用于组合布尔表达式。

  • &&(与)

    • 功能:两个操作数都为真时返回真。
    • 使用场景:多条件判断。
    • 注意事项:短路求值(左边为假不计算右边)。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      bool a = true, b = false;
      bool result = (a && b);
      std::cout << "true && false: " << result << "\n"; // 0
      return 0;
      }
  • ||(或)

    • 功能:任一操作数为真时返回真。
    • 注意事项:短路求值(左边为真不计算右边)。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      bool a = true, b = false;
      bool result = (a || b);
      std::cout << "true || false: " << result << "\n"; // 1
      return 0;
      }
  • !(非)

    • 功能:反转布尔值。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      bool a = true;
      bool result = !a;
      std::cout << "!true: " << result << "\n"; // 0
      return 0;
      }

位运算符

按位操作,直接操作二进制位。

  • &(按位与)

    • 功能:逐位进行与运算。
    • 使用场景:提取特定位、掩码操作。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      #include <iostream>
      int main() {
      int a = 5; // 0101
      int b = 3; // 0011
      int result = a & b; // 0001
      std::cout << "5 & 3 = " << result << "\n"; // 1
      return 0;
      }
  • |(按位或)

    • 功能:逐位进行或运算。
    • 使用场景:设置特定位。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      #include <iostream>
      int main() {
      int a = 5; // 0101
      int b = 3; // 0011
      int result = a | b; // 0111
      std::cout << "5 | 3 = " << result << "\n"; // 7
      return 0;
      }
  • ^(按位异或)

    • 功能:逐位异或,相同为 0,不同为 1。
    • 使用场景:交换值、检测差异。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      #include <iostream>
      int main() {
      int a = 5; // 0101
      int b = 3; // 0011
      int result = a ^ b; // 0110
      std::cout << "5 ^ 3 = " << result << "\n"; // 6
      return 0;
      }
  • ~(按位取反)

    • 功能:将所有位取反。
    • 注意事项:结果为补码形式。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 5; // 0101
      int result = ~a; // 1010(补码表示为 -6)
      std::cout << "~5 = " << result << "\n"; // -6
      return 0;
      }
  • <<(左移)

    • 功能:将位向左移动,低位补 0。
    • 使用场景:快速乘以 2 的幂。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 5; // 0101
      int result = a << 1; // 1010
      std::cout << "5 << 1 = " << result << "\n"; // 10
      return 0;
      }
  • >>(右移)

    • 功能:将位向右移动,符号位决定高位补 0 或 1。
    • 使用场景:快速除以 2 的幂。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 5; // 0101
      int result = a >> 1; // 0010
      std::cout << "5 >> 1 = " << result << "\n"; // 2
      return 0;
      }

赋值运算符

用于修改变量值。

  • =(赋值)

    • 功能:将右值赋给左值。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      #include <iostream>
      int main() {
      int a = 10;
      std::cout << "a = " << a << "\n"; // 10
      return 0;
      }
  • +=(加赋值)

    • 功能:加后赋值。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 10;
      a += 5; // a = a + 5
      std::cout << "a += 5: " << a << "\n"; // 15
      return 0;
      }
  • -=(减赋值)

    • 功能:减后赋值。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 10;
      a -= 3; // a = a - 3
      std::cout << "a -= 3: " << a << "\n"; // 7
      return 0;
      }
  • 其他赋值运算符*=/=%= 等类似)

    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int a = 10;
      a *= 2; // a = a * 2
      std::cout << "a *= 2: " << a << "\n"; // 20
      return 0;
      }

其他运算符

一些特殊运算符。

  • sizeof

    • 功能:返回类型或变量的字节大小。
    • 使用场景:内存分配、调试。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      int main() {
      int x = 10;
      std::cout << "sizeof(int): " << sizeof(int) << "\n"; // 通常 4
      std::cout << "sizeof(x): " << sizeof(x) << "\n"; // 4
      return 0;
      }
  • typeid

    • 功能:返回类型信息,需包含 <typeinfo>
    • 使用场景:运行时类型检查。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      #include <typeinfo>
      int main() {
      int x = 10;
      std::cout << "Type of x: " << typeid(x).name() << "\n"; // "i" (int)
      return 0;
      }
  • dynamic_cast

    • 功能:运行时类型转换,用于多态类型。
    • 使用场景:安全的向下转型。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #include <iostream>
      class Base { virtual void f() {} };
      class Derived : public Base {};
      int main() {
      Base* b = new Derived();
      Derived* d = dynamic_cast<Derived*>(b);
      if (d) std::cout << "Cast successful\n";
      delete b;
      return 0;
      }

控制结构

控制结构用于管理程序的执行流程。

条件语句

  • ifelse ifelse
    • 功能:根据条件执行不同代码块。
    • 使用场景:分支逻辑。
    • 底层原理:条件表达式求值为真(非 0)时执行。
    • 注意事项:避免悬垂 else 问题。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      #include <iostream>
      int main() {
      int x = 10;
      if (x > 0) {
      std::cout << "Positive\n";
      } else if (x == 0) {
      std::cout << "Zero\n";
      } else {
      std::cout << "Negative\n";
      }
      return 0;
      }

开关语句

  • switchcasedefault
    • 功能:根据整数值跳转到对应分支。
    • 使用场景:多条件选择。
    • 底层原理:编译为跳转表或条件分支。
    • 注意事项:需要 break,否则会贯穿。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #include <iostream>
      int main() {
      int x = 2;
      switch (x) {
      case 1: std::cout << "One\n"; break;
      case 2: std::cout << "Two\n"; break;
      default: std::cout << "Other\n";
      }
      return 0;
      }

循环

  • for

    • 功能:固定次数循环,包含初始化、条件和增量。
    • 使用场景:数组遍历、计数。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      #include <iostream>
      int main() {
      for (int i = 0; i < 3; i++) {
      std::cout << i << " "; // 0 1 2
      }
      std::cout << "\n";
      return 0;
      }
  • while

    • 功能:条件为真时重复执行。
    • 使用场景:不确定次数的循环。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #include <iostream>
      int main() {
      int i = 0;
      while (i < 3) {
      std::cout << i << " "; // 0 1 2
      i++;
      }
      std::cout << "\n";
      return 0;
      }
  • do-while

    • 功能:至少执行一次,后检查条件。
    • 使用场景:需要至少运行一次的循环。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #include <iostream>
      int main() {
      int i = 0;
      do {
      std::cout << i << " "; // 0 1 2
      i++;
      } while (i < 3);
      std::cout << "\n";
      return 0;
      }

跳转

  • break

    • 功能:跳出当前循环或 switch。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #include <iostream>
      int main() {
      for (int i = 0; i < 5; i++) {
      if (i == 3) break;
      std::cout << i << " "; // 0 1 2
      }
      std::cout << "\n";
      return 0;
      }
  • continue

    • 功能:跳过本次循环,继续下一次。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #include <iostream>
      int main() {
      for (int i = 0; i < 5; i++) {
      if (i == 2) continue;
      std::cout << i << " "; // 0 1 3 4
      }
      std::cout << "\n";
      return 0;
      }
  • return

    • 功能:退出函数并返回值。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      #include <iostream>
      int add(int a, int b) {
      return a + b; // 返回并退出
      }
      int main() {
      std::cout << add(2, 3) << "\n"; // 5
      return 0;
      }
  • goto

    • 功能:跳转到指定标签。
    • 使用场景:复杂控制流(不推荐)。
    • 注意事项:易导致代码混乱。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #include <iostream>
      int main() {
      int x = 0;
      goto label;
      x = 1; // 被跳过
      label:
      std::cout << "x: " << x << "\n"; // 0
      return 0;
      }

函数

函数是可重用的代码块,支持多种特性。

  • 函数声明与定义

    • 功能:声明指定函数签名,定义实现具体逻辑。
    • 使用场景:模块化编程。
    • 底层原理:声明生成符号,定义分配代码段。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #include <iostream>
      void func(); // 声明
      int main() {
      func();
      return 0;
      }
      void func() { // 定义
      std::cout << "Function called\n";
      }
  • 参数传递

    • 值传递:传递副本,修改不影响原值。
      • 示例代码:
        1
        2
        3
        4
        5
        6
        7
        8
        #include <iostream>
        void byValue(int x) { x = 20; }
        int main() {
        int a = 10;
        byValue(a);
        std::cout << "a: " << a << "\n"; // 10
        return 0;
        }
    • 引用传递:传递别名,修改影响原值。
      • 示例代码:
        1
        2
        3
        4
        5
        6
        7
        8
        #include <iostream>
        void byReference(int& x) { x = 20; }
        int main() {
        int a = 10;
        byReference(a);
        std::cout << "a: " << a << "\n"; // 20
        return 0;
        }
  • 默认参数

    • 功能:为参数提供默认值,未传参时使用。
    • 注意事项:默认参数必须从右向左定义。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #include <iostream>
      void func(int x = 10, int y = 20) {
      std::cout << "x: " << x << ", y: " << y << "\n";
      }
      int main() {
      func(); // 10, 20
      func(5); // 5, 20
      func(5, 15); // 5, 15
      return 0;
      }
  • 函数重载

    • 功能:同名函数根据参数不同区分。
    • 底层原理:编译器通过名字修饰区分。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      #include <iostream>
      int add(int a, int b) { return a + b; }
      double add(double a, double b) { return a + b; }
      int main() {
      std::cout << add(1, 2) << "\n"; // 3
      std::cout << add(1.5, 2.5) << "\n"; // 4
      return 0;
      }
  • 内联函数

    • 功能:使用 inline 建议编译器内联展开,减少调用开销。
    • 使用场景:小型函数提升性能。
    • 注意事项:仅建议,编译器可能忽略。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      #include <iostream>
      inline int square(int x) { return x * x; }
      int main() {
      std::cout << square(5) << "\n"; // 25
      return 0;
      }

指针与引用

指针和引用是 C++ 的核心特性,用于操作内存。

  • 指针

    • 功能:存储变量地址,可间接访问和修改数据。
    • 使用场景:动态内存、函数参数。
    • 底层原理:地址是内存位置的整数表示。
    • 注意事项:未初始化指针或野指针危险。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #include <iostream>
      int main() {
      int x = 10;
      int* ptr = &x; // 指向 x 的地址
      std::cout << "Address: " << ptr << "\n";
      std::cout << "Value: " << *ptr << "\n"; // 10
      *ptr = 20; // 修改 x
      std::cout << "Modified x: " << x << "\n"; // 20
      return 0;
      }
  • 引用

    • 功能:变量的别名,直接操作原变量。
    • 使用场景:函数参数传递、简化代码。
    • 底层原理:编译器将其实现为指针,但语法更安全。
    • 注意事项:必须初始化,不能为空。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #include <iostream>
      int main() {
      int x = 10;
      int& ref = x; // ref 是 x 的别名
      std::cout << "ref: " << ref << "\n"; // 10
      ref = 20; // 修改 x
      std::cout << "x: " << x << "\n"; // 20
      return 0;
      }
  • 空指针

    • 功能:表示指针不指向任何地址,C++98 中用 NULL
    • 使用场景:初始化指针、检查有效性。
    • 注意事项NULL 是宏,通常为 0。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      #include <iostream>
      int main() {
      int* ptr = NULL;
      if (ptr == NULL) {
      std::cout << "Pointer is null\n";
      }
      return 0;
      }

类与对象(面向对象特性)

C++ 的面向对象特性是其区别于 C 的重要部分,支持封装、继承和多态。以下是对每个子特性的详细讲解。

类定义(classstruct

  • 功能:类是用户定义的类型,封装数据和行为;class 默认访问权限为 privatestruct 默认为 public
  • 使用场景:建模现实世界的实体,如人、车等。
  • 底层原理:类是编译器的蓝图,实例化后分配内存,成员按声明顺序排列。
  • 注意事项:注意内存对齐可能增加对象大小;classstruct 在语法上等价,仅默认访问权限不同。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include <iostream>
    class MyClass { // class 定义
    private:
    int x;
    public:
    MyClass(int val) : x(val) {} // 构造函数
    int getX() { return x; }
    };
    struct MyStruct { // struct 定义
    int y = 20; // 默认 public
    };
    int main() {
    MyClass obj(10);
    std::cout << "MyClass x: " << obj.getX() << "\n"; // 10
    MyStruct s;
    std::cout << "MyStruct y: " << s.y << "\n"; // 20
    return 0;
    }

访问控制(publicprivateprotected

  • 功能:控制类成员的访问权限:
    • public:任何地方可访问。
    • private:仅类内部和友元可访问。
    • protected:类内部和派生类可访问。
  • 使用场景:封装数据,隐藏实现细节。
  • 底层原理:访问控制由编译器在编译时检查,不影响运行时。
  • 注意事项:合理设计访问权限以保护数据一致性。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include <iostream>
    class MyClass {
    private:
    int x = 10; // 私有
    protected:
    int y = 20; // 受保护
    public:
    int z = 30; // 公有
    int getX() { return x; }
    int getY() { return y; }
    };
    int main() {
    MyClass obj;
    // std::cout << obj.x << "\n"; // 错误:私有
    // std::cout << obj.y << "\n"; // 错误:受保护
    std::cout << "z: " << obj.z << "\n"; // 30
    std::cout << "x: " << obj.getX() << "\n"; // 10
    std::cout << "y: " << obj.getY() << "\n"; // 20
    return 0;
    }

构造函数与析构函数

  • 功能
    • 构造函数:初始化对象,名称与类名相同,无返回值。
    • 析构函数:清理资源,名称为 ~类名,自动调用。
  • 使用场景:管理对象生命周期,如分配/释放动态内存。
  • 底层原理:构造函数在对象创建时由编译器调用,析构函数在对象销毁时调用(栈上自动,堆上需 delete)。
  • 注意事项:若未定义,编译器提供默认版本;动态内存需手动释放。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include <iostream>
    class MyClass {
    private:
    int* ptr;
    public:
    MyClass(int val) { // 构造函数
    ptr = new int(val);
    std::cout << "Constructor called, value: " << *ptr << "\n";
    }
    ~MyClass() { // 析构函数
    delete ptr;
    std::cout << "Destructor called\n";
    }
    int get() { return *ptr; }
    };
    int main() {
    MyClass obj(42); // 栈上对象,自动析构
    std::cout << "Value: " << obj.get() << "\n"; // 42
    return 0; // 输出 Constructor -> Value -> Destructor
    }

拷贝构造函数

  • 功能:以现有对象初始化新对象,形式为 Class(const Class&)
  • 使用场景:对象复制,如函数参数传递。
  • 底层原理:默认执行浅拷贝,自定义可实现深拷贝。
  • 注意事项:若有动态资源,需深拷贝以避免双重释放。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include <iostream>
    class MyClass {
    private:
    int* ptr;
    public:
    MyClass(int val) : ptr(new int(val)) {}
    MyClass(const MyClass& other) { // 拷贝构造函数
    ptr = new int(*other.ptr); // 深拷贝
    std::cout << "Copy constructor called\n";
    }
    ~MyClass() { delete ptr; }
    int get() { return *ptr; }
    };
    int main() {
    MyClass a(10);
    MyClass b = a; // 调用拷贝构造函数
    std::cout << "a: " << a.get() << ", b: " << b.get() << "\n"; // 10, 10
    return 0;
    }

成员函数与数据成员

  • 功能
    • 数据成员:类中的变量,存储对象状态。
    • 成员函数:类中的函数,操作数据成员。
  • 使用场景:实现类的行为和属性。
  • 底层原理:成员函数共享,所有对象调用同一代码;数据成员每个对象独立存储。
  • 注意事项:避免成员函数修改意外数据。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <iostream>
    class MyClass {
    public:
    int x = 10; // 数据成员
    void increment() { x++; } // 成员函数
    int getX() { return x; }
    };
    int main() {
    MyClass obj;
    std::cout << "Initial x: " << obj.getX() << "\n"; // 10
    obj.increment();
    std::cout << "After increment: " << obj.getX() << "\n"; // 11
    return 0;
    }

静态成员(static

  • 功能
    • 静态数据成员:类级别共享,所有对象共用。
    • 静态成员函数:无需对象即可调用,仅访问静态成员。
  • 使用场景:计数对象数量、工具函数。
  • 底层原理:静态成员存储在全局数据区,生命周期与程序相同。
  • 注意事项:静态数据成员需在类外定义。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <iostream>
    class MyClass {
    public:
    static int count; // 静态数据成员
    MyClass() { count++; }
    static int getCount() { return count; } // 静态成员函数
    };
    int MyClass::count = 0; // 类外定义
    int main() {
    MyClass a, b;
    std::cout << "Object count: " << MyClass::getCount() << "\n"; // 2
    return 0;
    }

友元(friend

  • 功能:允许外部函数或类访问私有成员。
  • 使用场景:实现紧密相关的类或函数。
  • 底层原理:编译器放宽访问限制。
  • 注意事项:过度使用破坏封装。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <iostream>
    class MyClass {
    private:
    int x = 10;
    friend void print(MyClass& obj); // 友元函数
    };
    void print(MyClass& obj) {
    std::cout << "x: " << obj.x << "\n";
    }
    int main() {
    MyClass obj;
    print(obj); // 10
    return 0;
    }

继承(单继承、多继承)

  • 功能
    • 单继承:一个基类派生一个子类。
    • 多继承:多个基类派生一个子类。
  • 使用场景:代码复用、建模层次关系。
  • 底层原理:子类对象包含基类子对象,多继承可能导致内存布局复杂。
  • 注意事项:多继承可能引发菱形继承问题。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #include <iostream>
    class Base {
    public:
    int x = 10;
    };
    class Derived : public Base { // 单继承
    public:
    int y = 20;
    };
    class Base2 {
    public:
    int z = 30;
    };
    class Multi : public Base, public Base2 { // 多继承
    };
    int main() {
    Derived d;
    std::cout << "x: " << d.x << ", y: " << d.y << "\n"; // 10, 20
    Multi m;
    std::cout << "x: " << m.x << ", z: " << m.z << "\n"; // 10, 30
    return 0;
    }

多态性(虚函数 virtual,纯虚函数 = 0

  • 功能
    • 虚函数:通过基类指针调用派生类实现。
    • 纯虚函数:定义抽象接口,派生类必须实现。
  • 使用场景:运行时多态,如插件系统。
  • 底层原理:虚函数通过虚表(vtable)实现,每个类一个虚表指针。
  • 注意事项:虚函数有运行时开销;纯虚函数使类抽象。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include <iostream>
    class Base {
    public:
    virtual void speak() { std::cout << "Base\n"; } // 虚函数
    virtual void pure() = 0; // 纯虚函数
    virtual ~Base() {} // 虚析构函数
    };
    class Derived : public Base {
    public:
    void speak() override { std::cout << "Derived\n"; }
    void pure() override { std::cout << "Pure implemented\n"; }
    };
    int main() {
    Base* ptr = new Derived();
    ptr->speak(); // Derived
    ptr->pure(); // Pure implemented
    delete ptr;
    return 0;
    }

抽象类与接口

  • 功能:含纯虚函数的类为抽象类,不能实例化;全纯虚函数类可作为接口。
  • 使用场景:定义通用接口,如策略模式。
  • 底层原理:抽象类阻止实例化,强制子类实现。
  • 注意事项:析构函数应为虚函数。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #include <iostream>
    class Interface {
    public:
    virtual void action() = 0; // 接口
    virtual ~Interface() {}
    };
    class Impl : public Interface {
    public:
    void action() override { std::cout << "Action\n"; }
    };
    int main() {
    Interface* ptr = new Impl();
    ptr->action(); // Action
    delete ptr;
    return 0;
    }

模板

模板支持泛型编程,使代码类型无关。

函数模板

  • 功能:定义泛型函数,适用于多种类型。
  • 使用场景:通用算法,如最大值计算。
  • 底层原理:编译器为每种类型生成具体函数。
  • 注意事项:类型需支持函数中使用的操作。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <iostream>
    template<typename T>
    T max(T a, T b) {
    return (a > b) ? a : b;
    }
    int main() {
    std::cout << "Max int: " << max(5, 3) << "\n"; // 5
    std::cout << "Max double: " << max(1.5, 2.5) << "\n"; // 2.5
    return 0;
    }

类模板

  • 功能:定义泛型类,成员类型可变。
  • 使用场景:容器类,如向量、列表。
  • 底层原理:编译器为每种类型实例化类。
  • 注意事项:模板定义和实现通常放在头文件中。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include <iostream>
    template<typename T>
    class Box {
    private:
    T value;
    public:
    Box(T v) : value(v) {}
    T get() { return value; }
    void set(T v) { value = v; }
    };
    int main() {
    Box<int> intBox(42);
    std::cout << "Int box: " << intBox.get() << "\n"; // 42
    Box<double> doubleBox(3.14);
    std::cout << "Double box: " << doubleBox.get() << "\n"; // 3.14
    return 0;
    }

异常处理

异常处理机制用于管理运行时错误。

trycatchthrow

  • 功能
    • throw:抛出异常。
    • try:包裹可能抛异常的代码。
    • catch:捕获并处理异常。
  • 使用场景:错误处理,如文件操作失败。
  • 底层原理:异常通过栈展开传递,调用析构函数。
  • 注意事项:未捕获的异常终止程序。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <iostream>
    #include <stdexcept>
    int main() {
    try {
    int x = -1;
    if (x < 0) {
    throw std::runtime_error("Negative value");
    }
    } catch (const std::exception& e) {
    std::cout << "Exception: " << e.what() << "\n"; // Negative value
    }
    return 0;
    }

标准异常类(如 std::exception

  • 功能:提供标准化的异常类型。
  • 使用场景:一致性错误处理。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <iostream>
    #include <stdexcept>
    int main() {
    try {
    throw std::out_of_range("Index out of bounds");
    } catch (const std::out_of_range& e) {
    std::cout << "Out of range: " << e.what() << "\n";
    } catch (const std::exception& e) {
    std::cout << "General exception: " << e.what() << "\n";
    }
    return 0;
    }

命名空间

命名空间用于组织代码,避免命名冲突。

定义(namespace

  • 功能:将标识符分组,限定作用域。
  • 使用场景:库开发、避免全局污染。
  • 底层原理:编译器通过命名空间修饰符号名。
  • 注意事项:嵌套命名空间增加复杂度。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <iostream>
    namespace MySpace {
    int x = 42;
    namespace Inner {
    int y = 10;
    }
    }
    int main() {
    std::cout << "x: " << MySpace::x << "\n"; // 42
    std::cout << "y: " << MySpace::Inner::y << "\n"; // 10
    return 0;
    }

使用(using

  • 功能:引入命名空间或特定符号。
  • 注意事项using namespace 可能导致冲突。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <iostream>
    namespace MySpace {
    int x = 42;
    }
    int main() {
    using MySpace::x; // 引入 x
    std::cout << "x: " << x << "\n"; // 42
    // using namespace MySpace; // 引入整个命名空间
    return 0;
    }

动态内存管理

C++ 支持手动管理堆内存。

newdelete

  • 功能
    • new:分配堆内存并构造对象。
    • delete:析构对象并释放内存。
  • 使用场景:动态对象生命周期管理。
  • 底层原理:调用内存分配器(如 malloc)和构造函数。
  • 注意事项:成对使用,避免内存泄漏。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <iostream>
    class MyClass {
    public:
    int x;
    MyClass(int val) : x(val) { std::cout << "Constructed\n"; }
    ~MyClass() { std::cout << "Destroyed\n"; }
    };
    int main() {
    MyClass* ptr = new MyClass(10);
    std::cout << "x: " << ptr->x << "\n"; // 10
    delete ptr; // 释放
    return 0;
    }

数组(new[]delete[]

  • 功能:分配和释放连续内存块。
  • 注意事项new[] 需用 delete[] 释放。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <iostream>
    int main() {
    int* arr = new int[3]{1, 2, 3}; // 分配并初始化
    for (int i = 0; i < 3; i++) {
    std::cout << arr[i] << " "; // 1 2 3
    }
    std::cout << "\n";
    delete[] arr; // 释放数组
    return 0;
    }

预处理器

预处理器在编译前处理代码。

宏定义(#define

  • 功能:定义常量或简单函数。
  • 使用场景:常量、调试开关。
  • 底层原理:文本替换,无类型检查。
  • 注意事项:避免复杂宏,易出错。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    #include <iostream>
    #define PI 3.14
    #define SQUARE(x) ((x) * (x)) // 注意括号
    int main() {
    std::cout << "PI: " << PI << "\n"; // 3.14
    std::cout << "Square(5): " << SQUARE(5) << "\n"; // 25
    return 0;
    }

条件编译(#ifdef#ifndef#endif

  • 功能:根据条件包含或排除代码。
  • 使用场景:跨平台代码、调试。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <iostream>
    #define DEBUG
    int main() {
    #ifdef DEBUG
    std::cout << "Debug mode\n";
    #else
    std::cout << "Release mode\n";
    #endif
    #ifndef RELEASE
    std::cout << "Not release\n";
    #endif
    return 0;
    }

文件包含(#include

  • 功能:引入头文件或源代码。
  • 注意事项:使用 <> 表示标准库,"" 表示用户文件。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    #include <iostream>  // 标准库
    #include "myheader.h" // 用户头文件(假设存在)
    int main() {
    std::cout << "Hello\n";
    return 0;
    }

2. 现代 C++ 新特性(C++11 及之后)

从这里开始,我将详细讲解现代 C++ 的特性,从 C++11 开始。

C++11

自动类型推导

  • auto

    • 功能:让编译器根据初始化表达式推导变量类型。
    • 使用场景:简化复杂类型声明,如迭代器。
    • 底层原理:编译时类型推导,不影响运行时。
    • 注意事项:需初始化;不改变类型安全。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      #include <iostream>
      #include <vector>
      int main() {
      auto i = 10; // int
      auto d = 3.14; // double
      std::vector<int> v = {1, 2, 3};
      for (auto it = v.begin(); it != v.end(); ++it) {
      std::cout << *it << " "; // 1 2 3
      }
      std::cout << "\ni: " << i << ", d: " << d << "\n";
      return 0;
      }
  • decltype

    • 功能:提取表达式的类型,用于声明变量。
    • 使用场景:模板编程、类型推导。
    • 底层原理:编译时分析表达式类型。
    • 注意事项:可与 auto 结合使用。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #include <iostream>
      int main() {
      int x = 10;
      decltype(x) y = 20; // y 是 int
      std::cout << "y: " << y << "\n"; // 20
      decltype(x + 3.14) z = 5.5; // z 是 double
      std::cout << "z: " << z << "\n"; // 5.5
      return 0;
      }

范围 for 循环

  • 功能:基于范围的循环,简化容器遍历。
  • 使用场景:数组、STL 容器遍历。
  • 底层原理:编译器将其转换为迭代器循环。
  • 注意事项:容器需支持 begin()end()
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #include <iostream>
    #include <vector>
    int main() {
    std::vector<int> v = {1, 2, 3};
    for (int x : v) { // 按值
    std::cout << x << " "; // 1 2 3
    }
    std::cout << "\n";
    for (int& x : v) { // 按引用修改
    x *= 2;
    }
    for (int x : v) {
    std::cout << x << " "; // 2 4 6
    }
    return 0;
    }

nullptr

  • 功能:替代 NULL,明确表示空指针。
  • 使用场景:初始化指针、检查有效性。
  • 底层原理nullptrnullptr_t 类型,避免整数转换问题。
  • 注意事项:比 NULL(0)更类型安全。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <iostream>
    void func(int) { std::cout << "int\n"; }
    void func(int*) { std::cout << "pointer\n"; }
    int main() {
    int* ptr = nullptr;
    if (ptr == nullptr) {
    std::cout << "Pointer is null\n";
    }
    // func(NULL); // 歧义,可能调用 int
    func(nullptr); // 明确调用 pointer
    return 0;
    }

智能指针

  • std::unique_ptr

    • 功能:独占所有权的智能指针,自动释放内存。
    • 使用场景:管理单一所有权的动态资源。
    • 底层原理:RAII 封装,析构时调用 delete
    • 注意事项:不可复制,只能移动。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      #include <iostream>
      #include <memory>
      int main() {
      std::unique_ptr<int> uptr = std::make_unique<int>(10);
      std::cout << "Value: " << *uptr << "\n"; // 10
      // std::unique_ptr<int> uptr2 = uptr; // 错误:不可复制
      std::unique_ptr<int> uptr2 = std::move(uptr); // 移动
      if (!uptr) std::cout << "uptr is null\n";
      std::cout << "uptr2: " << *uptr2 << "\n"; // 10
      return 0; // 自动释放
      }
  • std::shared_ptr

    • 功能:共享所有权的智能指针,引用计数管理。
    • 使用场景:多个对象共享资源。
    • 底层原理:引用计数为 0 时释放内存。
    • 注意事项:避免循环引用。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      #include <iostream>
      #include <memory>
      int main() {
      std::shared_ptr<int> sptr1 = std::make_shared<int>(20);
      std::cout << "sptr1: " << *sptr1 << "\n"; // 20
      {
      std::shared_ptr<int> sptr2 = sptr1; // 共享
      std::cout << "sptr2: " << *sptr2 << "\n"; // 20
      std::cout << "Use count: " << sptr1.use_count() << "\n"; // 2
      }
      std::cout << "After scope, sptr1: " << *sptr1 << "\n"; // 20
      return 0; // 引用计数为 0,释放
      }
  • std::weak_ptr

    • 功能:弱引用指针,解决 shared_ptr 循环引用。
    • 使用场景:配合 shared_ptr 管理复杂关系。
    • 底层原理:不增加引用计数,需通过 lock() 获取 shared_ptr
    • 注意事项:需检查有效性。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      #include <iostream>
      #include <memory>
      struct Node {
      std::shared_ptr<Node> next;
      std::weak_ptr<Node> prev; // 避免循环引用
      ~Node() { std::cout << "Node destroyed\n"; }
      };
      int main() {
      auto n1 = std::make_shared<Node>();
      auto n2 = std::make_shared<Node>();
      n1->next = n2;
      n2->prev = n1;
      return 0; // 正常销毁
      }

移动语义

  • 右值引用(T&&
    • 功能:绑定到右值(如临时对象),支持移动语义。
    • 使用场景:优化资源转移,避免拷贝。
    • 底层原理:右值引用延长临时对象生命周期。
    • 注意事项:区分左值和右值。
  • 移动构造函数和 std::move
    • 功能:转移资源所有权,减少深拷贝。
    • 底层原理:将资源指针转移,原对象置为可销毁状态。
    • 注意事项:移动后原对象状态需定义。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      #include <iostream>
      #include <utility>
      class MyClass {
      private:
      int* data;
      public:
      MyClass(int val = 0) : data(new int(val)) {
      std::cout << "Constructor\n";
      }
      MyClass(MyClass&& other) noexcept : data(other.data) { // 移动构造函数
      other.data = nullptr;
      std::cout << "Move constructor\n";
      }
      ~MyClass() {
      delete data;
      std::cout << "Destructor\n";
      }
      int get() const { return data ? *data : 0; }
      };
      int main() {
      MyClass a(10);
      MyClass b = std::move(a); // 移动
      std::cout << "a: " << a.get() << ", b: " << b.get() << "\n"; // 0, 10
      return 0;
      }

完美转发

  • 功能:通过 std::forward 和右值引用,保持参数的值类别(左值或右值)。
  • 使用场景:模板函数转发参数。
  • 底层原理:利用引用折叠规则(T&& 可绑定左值或右值)。
  • 注意事项:需与模板配合。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <iostream>
    #include <utility>
    void process(int& x) { std::cout << "Lvalue: " << x << "\n"; }
    void process(int&& x) { std::cout << "Rvalue: " << x << "\n"; }
    template<typename T>
    void forward(T&& arg) {
    process(std::forward<T>(arg));
    }
    int main() {
    int x = 10;
    forward(x); // Lvalue: 10
    forward(20); // Rvalue: 20
    return 0;
    }

Lambda 表达式

  • 功能:定义匿名函数,支持捕获外部变量。
  • 使用场景:回调、局部逻辑。
  • 底层原理:编译器生成闭包类。
  • 注意事项:捕获方式影响变量生命周期。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <iostream>
    int main() {
    int x = 10;
    auto func = [x]() { return x * 2; }; // 按值捕获
    std::cout << "By value: " << func() << "\n"; // 20
    auto refFunc = [&x]() { return x * 2; }; // 按引用捕获
    x = 20;
    std::cout << "By reference: " << refFunc() << "\n"; // 40
    return 0;
    }

模板改进

  • 可变参数模板

    • 功能:支持不定数量的模板参数。
    • 使用场景:通用函数,如打印。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #include <iostream>
      template<typename... Args>
      void print(Args... args) {
      (std::cout << ... << args); // C++17 折叠表达式
      }
      int main() {
      print(1, " ", 2.5, " ", "hello"); // 1 2.5 hello
      std::cout << "\n";
      return 0;
      }
  • 模板别名

    • 功能:使用 using 定义模板类型别名。
    • 使用场景:简化复杂类型。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #include <iostream>
      #include <vector>
      template<typename T>
      using Vec = std::vector<T>;
      int main() {
      Vec<int> v = {1, 2, 3};
      for (auto x : v) std::cout << x << " "; // 1 2 3
      std::cout << "\n";
      return 0;
      }

初始化改进

  • 统一初始化

    • 功能:使用 {} 初始化所有类型。
    • 使用场景:一致性初始化。
    • 注意事项:窄化转换(如 double 到 int)被禁止。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      #include <iostream>
      int main() {
      int x{10};
      double d{3.14};
      std::cout << "x: " << x << ", d: " << d << "\n";
      // int y{3.14}; // 错误:窄化转换
      return 0;
      }
  • 初始化列表

    • 功能:用 {} 初始化容器或对象。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      #include <iostream>
      #include <vector>
      int main() {
      std::vector<int> v{1, 2, 3};
      for (auto x : v) std::cout << x << " "; // 1 2 3
      std::cout << "\n";
      return 0;
      }

并发支持

  • 线程(std::thread

    • 功能:创建和管理线程。
    • 使用场景:并行任务。
    • 注意事项:需 joindetach
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      #include <iostream>
      #include <thread>
      void func() { std::cout << "Thread running\n"; }
      int main() {
      std::thread t(func);
      t.join(); // 等待线程结束
      return 0;
      }
  • 互斥锁(std::mutexstd::lock_guard

    • 功能:保护共享数据。
    • 示例代码:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      #include <iostream>
      #include <thread>
      #include <mutex>
      std::mutex mtx;
      int counter = 0;
      void increment() {
      std::lock_guard<std::mutex> lock(mtx);
      counter++;
      }
      int main() {
      std::thread t1(increment);
      std::thread t2(increment);
      t1.join();
      t2.join();
      std::cout << "Counter: " << counter << "\n"; // 2
      return 0;
      }

constexpr

  • 功能:定义编译时常量或函数。
  • 使用场景:优化性能、静态断言。
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    #include <iostream>
    constexpr int square(int x) { return x * x; }
    int main() {
    int arr[square(3)]; // 编译时计算 9
    std::cout << "Array size: " << sizeof(arr) / sizeof(int) << "\n"; // 9
    return 0;
    }

好的,我将继续详细展开 现代 C++ 新特性(C++11 及之后) 的剩余部分,从 C++14 开始,依次讲解 C++14C++17C++20 的所有特性。每个特性都会提供详细的功能描述、使用场景、底层原理、注意事项,并附上完整的示例代码。让我们开始!


C++14

C++14 是 C++11 的增量更新,增强了语言的易用性和表达能力。

泛型 Lambda

  • 功能:允许 Lambda 表达式的参数使用 auto,使其支持泛型。
  • 使用场景:需要处理多种类型的匿名函数,如通用回调。
  • 底层原理:编译器为 Lambda 生成一个模板化的闭包类,每个类型实例化一个具体函数。
  • 注意事项:提高了代码灵活性,但可能增加编译时间;需确保参数类型支持 Lambda 体内的操作。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <iostream>
    int main() {
    auto genericLambda = [](auto x) { // 参数 x 是泛型的
    return x + 1;
    };
    std::cout << "Int: " << genericLambda(5) << "\n"; // 6
    std::cout << "Double: " << genericLambda(3.14) << "\n"; // 4.14
    std::cout << "Char: " << genericLambda('A') << "\n"; // 'B' (ASCII 66)
    return 0;
    }

返回类型推导

  • 功能:允许函数使用 auto 作为返回类型,由函数体推导。
  • 使用场景:简化函数声明,尤其在返回值类型复杂时。
  • 底层原理:编译器根据 return 语句推导类型,所有返回路径必须一致。
  • 注意事项:不能用于声明(需定义);递归函数需显式返回类型。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <iostream>
    auto add(int a, int b) { // 返回类型推导为 int
    return a + b;
    }
    auto multiply(double a, double b) { // 返回类型推导为 double
    return a * b;
    }
    int main() {
    std::cout << "Add: " << add(2, 3) << "\n"; // 5
    std::cout << "Multiply: " << multiply(2.5, 3.0) << "\n"; // 7.5
    return 0;
    }

constexpr 扩展

  • 功能:扩展 constexpr 函数,支持更复杂的编译时计算(如循环、条件语句)。
  • 使用场景:需要编译时计算复杂表达式,如数学函数。
  • 底层原理:编译器在编译时执行函数,确保结果为常量。
  • 注意事项:函数体内限制放宽,但仍需满足常量表达式要求(如无动态内存分配)。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <iostream>
    constexpr int factorial(int n) { // 编译时计算阶乘
    int result = 1;
    for (int i = 1; i <= n; ++i) { // C++14 允许循环
    result *= i;
    }
    return result;
    }
    int main() {
    int arr[factorial(4)]; // 编译时计算 24
    std::cout << "Array size: " << sizeof(arr) / sizeof(int) << "\n"; // 24
    std::cout << "Factorial(4): " << factorial(4) << "\n"; // 24
    return 0;
    }

变量模板

  • 功能:允许定义模板化的变量,提供类型参数化的常量。
  • 使用场景:泛型常量,如类型相关的数学常数。
  • 底层原理:编译器为每种类型实例化变量。
  • 注意事项:需显式指定类型或推导。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    #include <iostream>
    template<typename T>
    constexpr T pi = T(3.1415926535); // 变量模板
    int main() {
    std::cout << "Float pi: " << pi<float> << "\n"; // 3.14159
    std::cout << "Double pi: " << pi<double> << "\n"; // 3.14159
    return 0;
    }

C++17

C++17 引入了更多实用特性,提升了语言的现代化程度。

结构化绑定

  • 功能:解构赋值,将聚合类型(如 pairtuple、结构体)的成员绑定到变量。
  • 使用场景:简化多返回值函数的使用。
  • 底层原理:编译器生成临时对象并解构,绑定到新变量。
  • 注意事项:需支持结构化绑定的类型(如 std::pair 或含 std::tuple_size 的类型)。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <iostream>
    #include <utility>
    int main() {
    std::pair<int, double> p(1, 2.5);
    auto [x, y] = p; // 结构化绑定
    std::cout << "x: " << x << ", y: " << y << "\n"; // 1, 2.5
    struct Point { int a; double b; };
    Point pt{3, 4.5};
    auto [a, b] = pt;
    std::cout << "a: " << a << ", b: " << b << "\n"; // 3, 4.5
    return 0;
    }

if 和 switch 初始化

  • 功能:允许在 ifswitch 语句中初始化变量,限制作用域。
  • 使用场景:临时变量仅用于条件判断。
  • 底层原理:编译器将初始化和条件组合为单一语句。
  • 注意事项:变量作用域限于 ifswitch 块。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <iostream>
    int main() {
    if (int x = 10; x > 0) { // 初始化并判断
    std::cout << "x is positive: " << x << "\n"; // 10
    }
    // std::cout << x << "\n"; // 错误:x 超出作用域
    switch (int y = 2; y) { // 初始化并切换
    case 2: std::cout << "y is 2\n"; break;
    default: std::cout << "Other\n";
    }
    return 0;
    }

折叠表达式

  • 功能:简化可变参数模板的处理,允许对参数包进行一元或二元操作。
  • 使用场景:批量操作参数,如求和、打印。
  • 底层原理:编译器展开参数包并应用操作符。
  • 注意事项:支持常见运算符(如 +*&& 等)。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <iostream>
    template<typename... Args>
    auto sum(Args... args) {
    return (args + ...); // 二元左折叠
    }
    template<typename... Args>
    void print(Args... args) {
    (std::cout << ... << args); // 打印所有参数
    }
    int main() {
    std::cout << "Sum: " << sum(1, 2, 3, 4) << "\n"; // 10
    print("Hello", " ", 42, "\n"); // Hello 42
    return 0;
    }

std::optional

  • 功能:表示可能为空的值,提供类型安全的可选值。
  • 使用场景:函数可能无返回值,或值可选。
  • 底层原理:封装值和状态,析构时自动清理。
  • 注意事项:需检查是否有值(has_value()*)。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <iostream>
    #include <optional>
    std::optional<int> maybeInt(int x) {
    if (x > 0) return x;
    return std::nullopt; // 无值
    }
    int main() {
    auto opt1 = maybeInt(5);
    if (opt1) std::cout << "Value: " << *opt1 << "\n"; // 5
    auto opt2 = maybeInt(-1);
    if (!opt2) std::cout << "No value\n"; // No value
    return 0;
    }

std::variant

  • 功能:类型安全的联合类型,可存储多种类型之一。
  • 使用场景:替代 union,如状态机。
  • 底层原理:存储当前类型索引和值,析构时调用正确析构函数。
  • 注意事项:访问需使用 std::getstd::visit,错误访问抛异常。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <iostream>
    #include <variant>
    int main() {
    std::variant<int, double, std::string> v = 42;
    std::cout << "Int: " << std::get<int>(v) << "\n"; // 42
    v = 3.14;
    std::cout << "Double: " << std::get<double>(v) << "\n"; // 3.14
    v = "Hello";
    std::cout << "String: " << std::get<std::string>(v) << "\n"; // Hello
    // std::get<int>(v); // 抛出 std::bad_variant_access
    return 0;
    }

std::any

  • 功能:存储任意类型的值,提供类型擦除。
  • 使用场景:需要动态类型的场景,如脚本引擎。
  • 底层原理:使用类型擦除技术,存储值和类型信息。
  • 注意事项:访问需使用 std::any_cast,类型错误抛异常。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #include <iostream>
    #include <any>
    int main() {
    std::any a = 42;
    std::cout << "Int: " << std::any_cast<int>(a) << "\n"; // 42
    a = 3.14;
    std::cout << "Double: " << std::any_cast<double>(a) << "\n"; // 3.14
    a = std::string("Hello");
    std::cout << "String: " << std::any_cast<std::string>(a) << "\n"; // Hello
    try {
    std::any_cast<int>(a); // 抛出 std::bad_any_cast
    } catch (const std::bad_any_cast& e) {
    std::cout << "Bad cast: " << e.what() << "\n";
    }
    return 0;
    }

文件系统库(std::filesystem

  • 功能:提供文件和目录操作的标准化接口。
  • 使用场景:文件管理、路径处理。
  • 底层原理:封装操作系统文件 API。
  • 注意事项:需链接文件系统库(如 -lstdc++fs 在某些编译器)。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include <iostream>
    #include <filesystem>
    namespace fs = std::filesystem;
    int main() {
    fs::path p = "example.txt";
    if (fs::exists(p)) {
    std::cout << p << " exists\n";
    } else {
    std::cout << p << " does not exist\n";
    }
    for (const auto& entry : fs::directory_iterator(".")) {
    std::cout << entry.path() << "\n"; // 列出当前目录
    }
    return 0;
    }

并行算法

  • 功能:STL 算法支持并行执行,优化多核性能。
  • 使用场景:大数据排序、变换。
  • 底层原理:依赖线程池或底层并发支持。
  • 注意事项:需编译器支持(如 -ltbb)。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <execution>
    int main() {
    std::vector<int> v = {3, 1, 4, 1, 5};
    std::sort(std::execution::par, v.begin(), v.end()); // 并行排序
    for (auto x : v) std::cout << x << " "; // 1 1 3 4 5
    std::cout << "\n";
    return 0;
    }

inline 变量

  • 功能:允许在头文件中定义 inline 静态变量,避免多重定义。
  • 使用场景:头文件中的全局常量。
  • 底层原理:编译器保证单一实例。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // header.h
    #ifndef HEADER_H
    #define HEADER_H
    inline int globalVar = 42;
    #endif
    // main.cpp
    #include <iostream>
    #include "header.h"
    int main() {
    std::cout << "globalVar: " << globalVar << "\n"; // 42
    return 0;
    }

C++20

C++20 是近年来最大的更新,引入了许多革新特性。

概念(Concepts)

  • 功能:约束模板参数,提供类型检查。
  • 使用场景:泛型编程中确保类型满足要求。
  • 底层原理:编译时验证类型特性。
  • 注意事项:需支持 C++20 的编译器。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <iostream>
    #include <concepts>
    template<typename T>
    requires std::integral<T> // 约束 T 为整数类型
    T add(T a, T b) {
    return a + b;
    }
    int main() {
    std::cout << "Int: " << add(2, 3) << "\n"; // 5
    // add(2.5, 3.5); // 错误:double 不满足 integral
    return 0;
    }

Ranges 库

  • 功能:提供范围操作,增强 STL 的功能性。
  • 使用场景:链式处理容器数据。
  • 底层原理:基于迭代器,新增视图概念。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <iostream>
    #include <vector>
    #include <ranges>
    int main() {
    std::vector<int> v = {3, 1, 4, 1, 5};
    auto even = v | std::views::filter([](int x) { return x % 2 == 0; });
    std::ranges::sort(v);
    std::cout << "Sorted: ";
    for (auto x : v) std::cout << x << " "; // 1 1 3 4 5
    std::cout << "\nEven: ";
    for (auto x : even) std::cout << x << " "; // 4
    std::cout << "\n";
    return 0;
    }

协程(Coroutines)

  • 功能:支持异步编程,允许函数暂停和恢复。
  • 使用场景:异步 I/O、生成器。
  • 底层原理:编译器生成状态机,依赖协程框架。
  • 注意事项:需库支持(如 cppcoro),示例简化。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    #include <iostream>
    #include <coroutine>
    struct Generator {
    struct promise_type {
    int value;
    std::suspend_always initial_suspend() { return {}; }
    std::suspend_always final_suspend() noexcept { return {}; }
    void unhandled_exception() {}
    Generator get_return_object() { return {this}; }
    std::suspend_always yield_value(int v) { value = v; return {}; }
    void return_void() {}
    };
    std::coroutine_handle<promise_type> coro;
    Generator(promise_type* p) : coro(std::coroutine_handle<promise_type>::from_promise(*p)) {}
    ~Generator() { if (coro) coro.destroy(); }
    int next() { coro.resume(); return coro.promise().value; }
    };
    Generator generate() {
    for (int i = 0; i < 3; ++i) {
    co_yield i; // 暂停并返回值
    }
    }
    int main() {
    auto g = generate();
    for (int i = 0; i < 3; ++i) {
    std::cout << g.next() << " "; // 0 1 2
    }
    std::cout << "\n";
    return 0;
    }

模块(Modules)

  • 功能:替代头文件,提供模块化编程。
  • 使用场景:大型项目,减少编译依赖。
  • 底层原理:编译器生成模块单元,优化编译。
  • 注意事项:需 C++20 支持,主流编译器支持尚不完善。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // hello.cppm
    export module hello;
    export void say_hello() {
    std::cout << "Hello from module\n";
    }
    // main.cpp
    import hello;
    int main() {
    say_hello(); // Hello from module
    return 0;
    }

三路比较运算符(<=>

  • 功能:提供默认比较运算符,返回比较结果。
  • 使用场景:简化自定义类型的比较。
  • 底层原理:返回 std::strong_ordering 等类型。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <iostream>
    #include <compare>
    struct Point {
    int x;
    auto operator<=>(const Point& other) const = default; // 默认比较
    };
    int main() {
    Point p1{1}, p2{2};
    std::cout << "p1 < p2: " << (p1 < p2) << "\n"; // 1
    std::cout << "p1 == p2: " << (p1 == p2) << "\n"; // 0
    return 0;
    }

consteval 和 constinit

  • 功能
    • consteval:强制编译时计算。
    • constinit:控制常量初始化。
  • 使用场景:编译时优化、初始化控制。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    #include <iostream>
    consteval int square(int x) { return x * x; } // 必须编译时计算
    constinit int global = square(5); // 确保编译时初始化
    int main() {
    std::cout << "Square(3): " << square(3) << "\n"; // 9
    std::cout << "Global: " << global << "\n"; // 25
    return 0;
    }

新工具

  • std::span

    • 功能:提供连续内存的视图。
    • 使用场景:操作数组或容器片段。
    • 示例代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      #include <iostream>
      #include <span>
      void print(std::span<int> s) {
      for (auto x : s) std::cout << x << " ";
      std::cout << "\n";
      }
      int main() {
      int arr[] = {1, 2, 3, 4, 5};
      std::span<int> s(arr, 3); // 前 3 个元素
      print(s); // 1 2 3
      return 0;
      }
  • std::bit_cast

    • 功能:类型安全的位转换。
    • 使用场景:低级数据处理。
    • 示例代码
      1
      2
      3
      4
      5
      6
      7
      8
      #include <iostream>
      #include <bit>
      int main() {
      float f = 1.0f;
      uint32_t i = std::bit_cast<uint32_t>(f);
      std::cout << "Float as uint32_t: " << i << "\n"; // 1065353216
      return 0;
      }

日历和时区支持(std::chrono 扩展)

  • 功能:支持日期和时区操作。
  • 使用场景:时间相关应用。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <iostream>
    #include <chrono>
    int main() {
    using namespace std::chrono;
    auto now = system_clock::now();
    std::cout << "Now: " << now.time_since_epoch().count() << "\n";
    auto today = floor<days>(now);
    std::cout << "Year: " << year_month_day{today}.year() << "\n";
    return 0;
    }

3. 其他特性总结

标准库扩展

  • 功能:STL 提供容器(如 vector)、算法、I/O 等。
  • 使用场景:日常编程。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <iostream>
    #include <vector>
    #include <string>
    int main() {
    std::vector<int> v = {1, 2, 3};
    std::string s = "Hello";
    std::cout << "Vector: ";
    for (auto x : v) std::cout << x << " "; // 1 2 3
    std::cout << "\nString: " << s << "\n"; // Hello
    return 0;
    }

编译器特性

  • 属性([[nodiscard]] 等)
    • 功能:提供编译器提示。

    • 示例代码

      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      [[nodiscard]] int func() { return 42; }
      int main() {
      // func(); // 警告:忽略返回值
      std::cout << func() << "\n"; // 42
      return 0;
      }

问题研究

C++ 如何实现多态

在 C++ 中,多态(Polymorphism)是面向对象编程的核心特性之一,它允许在运行时根据对象的实际类型执行不同的行为。C++ 主要通过 虚函数(Virtual Functions)继承(Inheritance) 实现多态,尤其是 运行时多态(Runtime Polymorphism),也称为动态多态。下面我将详细讲解 C++ 如何实现多态及其底层原理,包括实现机制、使用场景、示例代码和注意事项。


1. C++ 多态的分类

C++ 中的多态分为两种:

  • 编译时多态(Compile-time Polymorphism)

    • 通过 函数重载(Function Overloading)运算符重载(Operator Overloading) 实现。
    • 在编译时根据参数类型或数量确定调用哪个函数。
    • 原理:编译器通过名称修饰(Name Mangling)生成唯一的函数签名。
    • 示例(不展开,因为问题聚焦运行时多态):
      1
      2
      3
      4
      5
      6
      7
      8
      #include <iostream>
      void print(int x) { std::cout << "Int: " << x << "\n"; }
      void print(double x) { std::cout << "Double: " << x << "\n"; }
      int main() {
      print(5); // Int: 5
      print(3.14); // Double: 3.14
      return 0;
      }
  • 运行时多态(Runtime Polymorphism)

    • 通过 虚函数基类指针/引用 实现。
    • 在运行时根据对象的实际类型决定调用哪个函数。
    • 这是本文重点讲解的内容。

2. 运行时多态的实现方式

C++ 通过以下机制实现运行时多态:

  • 继承:定义基类和派生类,形成层次结构。
  • 虚函数:在基类中使用 virtual 关键字声明函数,派生类可以重写(Override)。
  • 基类指针或引用:通过基类类型的指针或引用调用虚函数。

关键字和语法

  • virtual:标记函数为虚函数,启用动态分派。
  • override(C++11):显式声明派生类重写基类虚函数(可选,但推荐)。
  • final(C++11):禁止进一步重写虚函数或继承类(可选)。

基本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
class Base {
public:
virtual void speak() { // 虚函数
std::cout << "Base speaking\n";
}
virtual ~Base() {} // 虚析构函数(重要)
};

class Derived : public Base {
public:
void speak() override { // 重写虚函数
std::cout << "Derived speaking\n";
}
};

int main() {
Base* ptr = new Derived(); // 基类指针指向派生类对象
ptr->speak(); // 输出 "Derived speaking"
delete ptr; // 正确调用 Derived 的析构函数
return 0;
}

运行结果:

1
Derived speaking

在这个例子中,尽管 ptrBase* 类型,但调用 speak() 时执行了 Derived 的版本,这就是运行时多态。


3. 多态的底层原理:虚函数表(vtable)

C++ 使用 虚函数表(Virtual Function Table,简称 vtable)虚表指针(vptr) 来实现运行时多态。以下是详细原理:

3.1 虚函数表的概念

  • 定义:每个包含虚函数的类在编译时会生成一个虚函数表(vtable),这是一个函数指针数组,存储该类所有虚函数的地址。
  • 内容:vtable 中按声明顺序存储虚函数的地址,派生类重写虚函数时会替换对应位置的地址。
  • 唯一性:每个类(而不是每个对象)拥有一个唯一的 vtable,共享给所有该类实例。

3.2 虚表指针(vptr)

  • 定义:每个包含虚函数的对象在内存中额外存储一个指向其类 vtable 的指针(vptr)。
  • 位置:vptr 通常存储在对象内存布局的开头(具体位置由编译器决定)。
  • 初始化:在对象构造时,构造函数会将 vptr 设置为指向该类的 vtable。

3.3 运行时分派的流程

  1. 对象创建
    • 创建 Derived 对象时,Derived 的构造函数将 vptr 设置为指向 Derived 的 vtable。
    • Derived 的 vtable 中,speak() 的地址是 Derived::speak 的实现。
  2. 虚函数调用
    • 通过 Base* ptr 调用 ptr->speak()
    • 编译器生成代码,访问 ptr 指向对象的 vptr。
    • 根据 vptr 找到 Derived 的 vtable。
    • 从 vtable 中提取 speak() 的函数地址(偏移固定,由编译器确定)。
    • 调用该地址对应的函数(即 Derived::speak)。
  3. 销毁对象
    • 删除对象时,虚析构函数确保按正确顺序调用析构函数(从派生类到基类)。

内存布局示意图

假设 BaseDerived 的定义如上:

  • Base 类 vtable
    1
    2
    [0]: Base::speak
    [1]: Base::~Base
  • Derived 类 vtable
    1
    2
    [0]: Derived::speak  // 重写
    [1]: Derived::~Derived
  • 对象内存
    1
    2
    3
    Derived 对象:
    | vptr (指向 Derived 的 vtable) |
    | 数据成员(若有) |

3.4 编译器如何处理

  • 静态代码:编译器为每个虚函数调用生成间接调用指令(如 call [vptr + offset])。
  • 动态分派:运行时通过 vptr 和 vtable 确定实际函数地址。

4. 详细示例与验证

以下代码展示多态的实现,并通过调试手段验证 vtable 的存在:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
class Base {
public:
virtual void func1() { std::cout << "Base::func1\n"; }
virtual void func2() { std::cout << "Base::func2\n"; }
virtual ~Base() { std::cout << "Base destroyed\n"; }
};

class Derived : public Base {
public:
void func1() override { std::cout << "Derived::func1\n"; }
void func2() override { std::cout << "Derived::func2\n"; }
~Derived() override { std::cout << "Derived destroyed\n"; }
};

int main() {
Base* b1 = new Base();
Base* b2 = new Derived();

std::cout << "Calling through Base pointer:\n";
b1->func1(); // Base::func1
b1->func2(); // Base::func2
b2->func1(); // Derived::func1
b2->func2(); // Derived::func2

delete b1; // Base destroyed
delete b2; // Derived destroyed -> Base destroyed

// 使用引用
Derived d;
Base& ref = d;
ref.func1(); // Derived::func1

return 0;
}

运行结果:

1
2
3
4
5
6
7
8
9
Calling through Base pointer:
Base::func1
Base::func2
Derived::func1
Derived::func2
Base destroyed
Derived destroyed
Base destroyed
Derived::func1
  • 验证 vtable:在调试器(如 GDB 或 Visual Studio)中检查对象地址,会发现额外指针(vptr),其值指向 vtable。可以用 sizeof 检查对象大小:
    1
    2
    std::cout << "Size of Base: " << sizeof(Base) << "\n";    // 通常 8(vptr)
    std::cout << "Size of Derived: " << sizeof(Derived) << "\n"; // 通常 8(共享 vptr)

5. 注意事项与限制

  • 虚函数的开销
    • 空间开销:每个对象增加一个 vptr(通常 4 或 8 字节),每个类一个 vtable。
    • 时间开销:虚函数调用需要间接寻址,比直接调用慢。
  • 虚析构函数
    • 如果基类析构函数不是虚函数,通过基类指针删除派生类对象只会调用基类析构函数,导致资源泄漏。
    • 示例(错误情况):
      1
      2
      3
      4
      5
      6
      7
      class Base { public: ~Base() {} };  // 非虚析构
      class Derived : public Base { public: ~Derived() { std::cout << "Derived\n"; } };
      int main() {
      Base* ptr = new Derived();
      delete ptr; // 只调用 Base::~Base,Derived::~Derived 被跳过
      return 0;
      }
  • 纯虚函数(= 0
    • 定义抽象类,强制派生类实现。
    • 示例:
      1
      2
      3
      4
      class Abstract {
      public:
      virtual void mustImplement() = 0; // 纯虚函数
      };
  • 非虚函数:通过基类指针调用时,始终执行基类版本,不实现多态。
  • 多重继承
    • 多继承下,每个基类的 vtable 独立,对象可能有多个 vptr。

6. 多态的使用场景

  • 插件系统:基类定义接口,派生类实现具体功能。
  • 策略模式:运行时切换行为。
  • 框架设计:如 GUI 框架中,基类 Widget,派生类 ButtonTextBox

7. 总结

  • 如何实现:C++ 通过虚函数和基类指针/引用实现运行时多态。
  • 原理:依赖虚函数表(vtable)和虚表指针(vptr),编译器生成间接调用,运行时根据对象类型分派。
  • 关键点:需要 virtual 关键字、继承体系和动态绑定。
  • 优势:灵活性高,支持扩展。
  • 代价:增加内存和性能开销。

如果需要更深入探讨(如 vtable 的内存布局、多继承下的实现,或性能分析),请告诉我,我可以进一步扩展!

8. 示意图

好的,我将使用 Mermaid 语法为你绘制 C++ 多态底层原理的图示,展示 虚函数表(vtable)虚表指针(vptr) 的工作机制。由于 Mermaid 是一种基于文本的绘图工具,我会尽量清晰地表达多态的内存布局和调用流程。

以下是两种图:

  1. 类层次结构和 vtable 的关系图:展示基类和派生类的虚函数表。
  2. 对象内存布局和调用流程图:展示对象中的 vptr 如何指向 vtable 并实现动态分派。

图 1:类层次结构和虚函数表的关系

这个图展示了 BaseDerived 类的继承关系,以及它们的 vtable 如何存储虚函数地址。

继承

Base

-vptr

+speak()

+~Base()

Derived

-vptr

+speak()

+~Derived()

Base_vtable

+speak

+~Base

Derived_vtable

+speak

+~Derived

解释
  • Base 和 Derived 类Base 是基类,Derived 是派生类,通过继承关系连接。
  • 虚函数表(vtable)
    • Base_vtable:存储 Base::speakBase::~Base 的地址。
    • Derived_vtable:存储 Derived::speakDerived::~Derived 的地址,speak 被重写。
  • vptr:每个类实例包含一个虚表指针,指向其对应的 vtable。

图 2:对象内存布局和调用流程

这个图展示了 Base* ptr = new Derived() 的内存布局和运行时调用 ptr->speak() 的流程。

Derived_vtablevptrDerived 对象Base* ptrmain()Derived_vtablevptrDerived 对象Base* ptrmain()分配 Derived 对象ptr = new Derived()构造 Derived设置 vptr 指向 Derived_vtable关联ptr->speak()访问对象获取 vptr查找 vtable调用 Derived::speak()delete ptr析构获取 vptr调用 Derived::~Derived()完成析构
内存布局示意图

由于 Mermaid 不直接支持内存布局图,我用文本描述并结合上图解释:

1
2
3
4
5
6
7
8
9
10
11
12
Derived 对象内存布局:
+-------------------+
| vptr (指向 Derived_vtable) | <- 虚表指针
+-------------------+
| 数据成员(若有) |
+-------------------+

Derived_vtable:
+-------------------+
| [0] Derived::speak | <- 虚函数地址
| [1] Derived::~Derived |
+-------------------+
解释
  • 对象创建
    • new Derived() 创建对象,构造函数设置 vptr 指向 Derived_vtable
  • 虚函数调用
    • ptr->speak():通过 ptr 访问对象,找到 vptr
    • vptr 指向 Derived_vtable,表中 [0]Derived::speak 的地址。
    • 调用该地址,执行 Derived::speak
  • 对象销毁
    • delete ptr:通过 vptr 找到 Derived_vtable,调用 Derived::~Derived

代码验证

以下代码与图对应,供参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
class Base {
public:
virtual void speak() { std::cout << "Base speaking\n"; }
virtual ~Base() { std::cout << "Base destroyed\n"; }
};

class Derived : public Base {
public:
void speak() override { std::cout << "Derived speaking\n"; }
~Derived() override { std::cout << "Derived destroyed\n"; }
};

int main() {
Base* ptr = new Derived();
ptr->speak(); // 通过 vtable 调用 Derived::speak
delete ptr; // 通过 vtable 调用 Derived::~Derived
return 0;
}

运行结果:

1
2
3
Derived speaking
Derived destroyed
Base destroyed

总结

  • 图 1 展示了类与 vtable 的静态关系,说明多态依赖继承和虚函数重写。
  • 图 2 展示了运行时动态分派的过程,强调 vptr 和 vtable 的作用。
  • 原理:C++ 通过虚函数表和虚表指针实现运行时多态,确保基类指针能正确调用派生类的函数。

如果需要调整图表内容、增加细节(如多继承的 vtable)或进一步解释,请告诉我!

C++ 的模板和 Java 的泛型比较

在编程语言中,C++ 的模板(Templates)和 Java 的泛型(Generics)都是为了实现代码的通用性和类型安全而设计的机制,但它们的实现方式、设计理念和使用场景有显著差异。以下从多个方面对它们进行比较,用中文详细讲解。


1. 基本概念

  • C++ 模板

    • C++ 的模板是一种编译时(compile-time)的机制,允许开发者编写与类型无关的代码。
    • 它是基于“代码生成”的思想:编译器在编译时根据模板参数生成具体的代码(称为“模板实例化”)。
    • 模板可以用于类(类模板)、函数(函数模板)以及变量(C++14 引入变量模板)。

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    template <typename T>
    T add(T a, T b) {
    return a + b;
    }
    int main() {
    int x = add(1, 2); // 实例化为 add<int>
    double y = add(1.5, 2.5); // 实例化为 add<double>
    return 0;
    }
  • Java 泛型

    • Java 的泛型是一种运行时(runtime)支持的机制,引入于 Java 5,主要用于提高类型安全性,避免运行时类型转换错误。
    • 它是基于“类型擦除”(type erasure)的实现:在编译时,泛型信息会被擦除,生成的字节码中只保留原始类型(raw type),通过类型检查确保安全性。
    • 泛型主要用于类、接口和方法。

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Box<T> {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
    }
    public static void main(String[] args) {
    Box<Integer> intBox = new Box<>();
    intBox.set(10);
    System.out.println(intBox.get());
    }

2. 实现机制

  • C++ 模板

    • 编译时实例化:每次使用不同的类型调用模板时,编译器会生成一份新的代码。例如,add<int>add<double> 会生成两份独立的函数。
    • 无类型擦除:模板保留所有类型信息,编译器完全知道每个实例的具体类型。
    • 鸭子类型(Duck Typing):模板不要求类型实现特定接口,只要代码在语法上有效(比如支持 + 操作),编译就能通过。这也可能导致晦涩的错误信息。
  • Java 泛型

    • 类型擦除:编译后,泛型类型信息被擦除,Box<Integer>Box<String> 在字节码中都是 Box,只不过编译器插入了必要的类型转换(如 (Integer))。
    • 运行时统一:由于擦除,运行时无法直接获取泛型参数的类型(需要通过反射绕过)。
    • 类型约束:泛型通常需要通过 extendssuper 指定类型边界,要求类型实现特定接口或继承特定类。

3. 灵活性与约束

  • C++ 模板

    • 灵活性极高:可以用于任何类型,甚至包括基本类型(如 intdouble),无需显式约束。
    • 支持非类型参数:模板不仅支持类型参数,还支持整数、指针等非类型参数。
      示例:
      1
      2
      3
      4
      5
      template <int N>
      struct Array {
      int data[N];
      };
      Array<5> arr; // 固定大小数组
    • 缺点:缺乏运行时类型检查,错误通常在编译时暴露,且错误信息可能复杂难懂。
  • Java 泛型

    • 约束较多:不支持基本类型(如 intdouble),必须使用包装类(如 IntegerDouble),因为泛型基于对象。
    • 不支持非类型参数:只能使用类型参数,无法像 C++ 那样用常量值作为模板参数。
    • 优点:通过类型擦除和编译时检查,保证了运行时的类型安全,且错误信息通常更直观。

4. 性能

  • C++ 模板

    • 零运行时开销:由于模板在编译时生成具体代码,运行时没有额外的类型检查或转换开销,性能几乎等同于手写特定类型的代码。
    • 代码膨胀:每个类型实例化都会生成新代码,可能导致二进制文件变大。
  • Java 泛型

    • 运行时开销:由于类型擦除和潜在的自动装箱/拆箱(如 intInteger),可能引入性能损耗。
    • 代码复用:字节码中只有一个类定义,不会因为泛型参数不同而重复生成代码,二进制文件更小。

5. 使用场景

  • C++ 模板

    • 高性能通用库:如 STL(标准模板库)中的容器(vectormap)和算法(sort),充分利用编译时优化。
    • 元编程:C++ 模板支持模板元编程(TMP),可以实现复杂的编译时计算。
      示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      template <int N>
      struct Factorial {
      static const int value = N * Factorial<N - 1>::value;
      };
      template <>
      struct Factorial<0> {
      static const int value = 1;
      };
      int main() {
      std::cout << Factorial<5>::value << std::endl; // 输出 120
      return 0;
      }
  • Java 泛型

    • 类型安全容器:如 List<T>Map<K, V>,避免运行时类型转换错误。
    • API 设计:泛型广泛用于框架和库(如 Java Collections Framework),提高代码可读性和安全性。
    • 不支持元编程:由于类型擦除和运行时限制,泛型无法实现像 C++ 那样的编译时计算。

6. 优缺点总结

特性 C++ 模板 Java 泛型
实现时机 编译时实例化 编译时检查,运行时擦除
类型支持 支持基本类型和对象类型 仅支持对象类型(需包装类)
灵活性 高,支持非类型参数和元编程 较低,约束较多
性能 无运行时开销,可能代码膨胀 有装箱/拆箱开销,代码复用好
错误检测 编译时,可能信息复杂 编译时,信息较清晰
运行时类型信息 保留 擦除,需反射获取

7. 实际例子对比

  • C++ 模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    template <typename T>
    class Container {
    public:
    T value;
    Container(T v) : value(v) {}
    void print() { std::cout << value << std::endl; }
    };
    int main() {
    Container<int> c1(42); // 实例化为 Container<int>
    Container<double> c2(3.14); // 实例化为 Container<double>
    c1.print(); // 输出 42
    c2.print(); // 输出 3.14
    return 0;
    }
  • Java 泛型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Container<T> {
    private T value;
    public Container(T v) { this.value = v; }
    public void print() { System.out.println(value); }
    }
    public static void main(String[] args) {
    Container<Integer> c1 = new Container<>(42);
    Container<Double> c2 = new Container<>(3.14);
    c1.print(); // 输出 42
    c2.print(); // 输出 3.14
    }
    • 区别:C++ 生成两个不同类(Container<int>Container<double>),Java 生成单一字节码,运行时靠类型检查。

8. 结论

  • C++ 模板更适合追求性能和灵活性的场景,尤其是嵌入式系统或高性能计算。它提供了强大的编译时能力,但需要开发者处理复杂的编译错误。
  • Java 泛型更适合注重类型安全和代码简洁性的场景,适用于企业级应用和需要运行时一致性的环境,但牺牲了一些灵活性和性能。

两者各有千秋,选择哪种取决于项目需求和语言生态。如果你有具体的应用场景想讨论,可以告诉我,我再深入分析!