程序设计基础(C++)

1. 基础cpp语法

├── 1.1 基本语法
│   ├── 程序结构
│   ├── 注释
│   ├── 关键字
│   └── 标识符命名规则
├── 1.2 数据类型
│   ├── 基本数据类型
│   │   ├── 整型(int, short, long, long long)
│   │   ├── 字符型(char, wchar_t)
│   │   ├── 浮点型(float, double)
│   │   ├── 布尔型(bool)
│   │   └── void类型
│   ├── 类型修饰符
│   │   ├── signed/unsigned
│   │   ├── const
│   │   └── volatile
│   └── 类型转换
│       ├── 隐式类型转换
│       ├── 显式类型转换(static_cast等)
│       └── 类型别名(typedef, using)
├── 1.3 变量与常量
│   ├── 变量声明与定义
│   ├── 常量(const, constexpr)
│   └── 作用域与生命周期
├── 1.4 运算符
│   ├── 算术运算符
│   ├── 关系运算符
│   ├── 逻辑运算符
│   ├── 位运算符
│   ├── 赋值运算符
│   ├── 条件运算符(?:)
│   ├── 逗号运算符
│   ├── sizeof运算符
│   └── 运算符优先级与结合性
└── 1.5 输入输出
    ├── cin/cout/cerr
    ├── 格式控制
    └── 文件流基础

1.1 程序基本结构

// 示例1.1:第一个C++程序
#include <iostream>  // 预处理指令,包含头文件

// 命名空间声明
using namespace std;  

/*
 * 多行注释
 * 主函数是程序的入口
 */
int main() {  // 程序从这里开始执行
    // 输出语句
    cout << "Hello, World!" << endl;

    // 返回0表示程序正常结束
    return 0;
}

易错点提示

  • #include <iostream> 不是语句,末尾没有分号
  • main() 函数必须返回 int 类型(C++标准)
  • 使用 endl"\n" 换行,两者有细微区别(endl会刷新缓冲区)

1.2 基本数据类型

// 示例1.2:基本数据类型
#include <iostream>
#include <iomanip>  // 用于格式化输出
using namespace std;

int main() {
    // 1. 整型
    int a = 10;           // 4字节,通常范围:-2,147,483,648 ~ 2,147,483,647
    short b = 100;        // 2字节,范围:-32,768 ~ 32,767
    long c = 1000L;       // 4字节或8字节,取决于平台
    long long d = 1000000LL;  // 8字节

    // 2. 字符型
    char ch1 = 'A';       // 单引号表示字符
    char ch2 = 65;        // ASCII码值,同样表示'A'
    wchar_t wch = L'中';  // 宽字符,用于Unicode

    // 3. 浮点型
    float f = 3.14f;      // 单精度,4字节,f后缀表示float
    double df = 3.14159;  // 双精度,8字节
    long double ldf = 3.141592653589793L;  // 扩展精度

    // 4. 布尔型
    bool flag1 = true;    // 实际存储为1
    bool flag2 = false;   // 实际存储为0

    // 5. void类型 - 无类型,不能定义void变量
    // void v;  // 错误!

    // 输出各类型大小
    cout << "sizeof(int) = " << sizeof(int) << " bytes" << endl;
    cout << "sizeof(char) = " << sizeof(char) << " bytes" << endl;
    cout << "sizeof(bool) = " << sizeof(bool) << " bytes" << endl;

    return 0;
}

易错点提示

  1. 整数溢出
   short s = 32767;
   s = s + 1;  // 溢出!变为-32768
  1. 浮点数比较
   float f1 = 0.1f;
   float f2 = 0.2f;
   float f3 = 0.3f;
   // 错误写法
   if (f1 + f2 == f3) {  // 可能不成立!浮点数有精度误差
       cout << "Equal" << endl;
   }
   // 正确写法
   if (abs(f1 + f2 - f3) < 0.00001) {
       cout << "Approximately equal" << endl;
   }

1.3 类型修饰符与类型转换

// 示例1.3:类型修饰符与转换
#include <iostream>
using namespace std;

int main() {
    // 1. signed/unsigned
    signed int s1 = -10;      // 有符号,可正可负
    unsigned int u1 = 10;     // 无符号,只能非负

    // 危险!负数赋给无符号数
    unsigned int u2 = -10;    // 实际值为4294967286(32位系统)
    cout << "u2 = " << u2 << endl;

    // 2. const - 常量
    const int MAX_SIZE = 100;
    // MAX_SIZE = 200;  // 错误!常量不能修改

    // 3. 类型转换
    // 隐式转换(自动转换)
    int i = 10;
    double d = i;  // int → double,安全

    double d2 = 3.14;
    int i2 = d2;   // double → int,丢失小数部分,警告!

    // 显式转换(强制转换)
    // C风格转换(不推荐)
    int i3 = (int)d2;

    // C++风格转换
    int i4 = static_cast<int>(d2);      // 静态转换,常用
    const int ci = 10;
    int* pi = const_cast<int*>(&ci);   // 去掉const属性,危险!

    // 4. 类型别名
    typedef int INTEGER;      // C风格
    INTEGER num = 100;

    using LL = long long;     // C++11风格
    LL big_num = 1000000000LL;

    return 0;
}

易混淆点

  1. 四种C++风格转换的区别
  • static_cast:常规转换,如数值类型转换、void*转换
  • dynamic_cast:用于多态类型的向下转换,运行时检查
  • const_cast:去掉const/volatile属性
  • reinterpret_cast:低级别重新解释,最危险
  1. signed与unsigned比较的陷阱
   int a = -1;
   unsigned int b = 10;
   if (a < b) {  // a被转换为unsigned,变成很大的正数,条件不成立!
       cout << "a < b" << endl;
   } else {
       cout << "a >= b" << endl;  // 输出这个!
   }

1.4 变量与常量

// 示例1.4:变量与常量
#include <iostream>
using namespace std;

// 全局变量
int global_var = 100;  // 初始化为100
const int GLOBAL_CONST = 200;

int main() {
    // 1. 变量声明与定义
    int a;            // 声明并定义,未初始化(值随机)
    int b = 10;       // 声明、定义并初始化
    int c(20);        // C++风格的初始化
    int d{30};        // C++11列表初始化
    int e = {40};     // C++11列表初始化

    // 2. 常量
    const int LOCAL_CONST = 50;
    // LOCAL_CONST = 60;  // 错误!

    // const与指针(难点!)
    int x = 10;
    int y = 20;

    const int* p1 = &x;  // p1指向常量,不能通过p1修改x
    // *p1 = 30;  // 错误!
    p1 = &y;      // 正确,p1本身可以指向别的变量

    int* const p2 = &x;  // p2是常量指针,不能指向别的变量
    *p2 = 30;     // 正确,可以通过p2修改x
    // p2 = &y;   // 错误!

    const int* const p3 = &x;  // p3是指向常量的常量指针
    // *p3 = 40;  // 错误!
    // p3 = &y;   // 错误!

    // 3. 作用域
    {
        int block_var = 100;
        cout << "block_var = " << block_var << endl;
    }
    // cout << block_var << endl;  // 错误!超出作用域

    // 4. 变量的生命周期
    static int static_var = 0;  // 静态局部变量,只初始化一次
    static_var++;
    cout << "static_var = " << static_var << endl;

    return 0;
}

易错点提示

  1. 未初始化变量的值
   int x;  // 值随机,可能是任意值
   cout << x << endl;  // 危险!
  1. const位置不同含义不同
   const int* p;     // 指向const int的指针(指针可变,指向的内容不可变)
   int const* p;     // 同上,等价写法
   int* const p;     // const指针(指针不可变,指向的内容可变)
   const int* const p;  // 指向const int的const指针

1.5 运算符

// 示例1.5:运算符
#include <iostream>
using namespace std;

int main() {
    // 1. 算术运算符
    int a = 10, b = 3;
    cout << "a + b = " << a + b << endl;  // 13
    cout << "a - b = " << a - b << endl;  // 7
    cout << "a * b = " << a * b << endl;  // 30
    cout << "a / b = " << a / b << endl;  // 3(整数除法)
    cout << "a % b = " << a % b << endl;  // 1(取余)

    // 2. 关系运算符
    cout << "a > b: " << (a > b) << endl;   // 1(true)
    cout << "a == b: " << (a == b) << endl; // 0(false)

    // 3. 逻辑运算符
    bool x = true, y = false;
    cout << "x && y: " << (x && y) << endl;  // 0(与)
    cout << "x || y: " << (x || y) << endl;  // 1(或)
    cout << "!x: " << (!x) << endl;          // 0(非)

    // 短路求值
    int i = 0;
    if (i != 0 && 10 / i > 0) {  // 短路,不会除以0
        cout << "This won't execute" << endl;
    }

    // 4. 位运算符
    unsigned char m = 0b11001100;  // 204
    unsigned char n = 0b10101010;  // 170
    cout << "m & n = " << (m & n) << endl;  // 136(0b10001000)
    cout << "m | n = " << (m | n) << endl;  // 238(0b11101110)
    cout << "m ^ n = " << (m ^ n) << endl;  // 102(0b01100110)
    cout << "~m = " << (~m) << endl;        // 51(0b00110011)
    cout << "m << 2 = " << (m << 2) << endl;  // 48(0b00110000)

    // 5. 赋值运算符
    int k = 10;
    k += 5;   // k = 15
    k -= 3;   // k = 12
    k *= 2;   // k = 24
    k /= 4;   // k = 6
    k %= 4;   // k = 2

    // 6. 自增自减运算符
    int p = 5;
    int q = ++p;  // 前置:先自增,再赋值
    cout << "p = " << p << ", q = " << q << endl;  // p=6, q=6

    int r = 5;
    int s = r++;  // 后置:先赋值,再自增
    cout << "r = " << r << ", s = " << s << endl;  // r=6, s=5

    // 7. 条件运算符(三目运算符)
    int max = (a > b) ? a : b;
    cout << "max = " << max << endl;

    // 8. 逗号运算符
    int t = (a = 5, b = 10, a + b);  // t = 15
    cout << "t = " << t << endl;

    // 9. sizeof运算符
    cout << "sizeof(int) = " << sizeof(int) << endl;
    cout << "sizeof(a) = " << sizeof(a) << endl;

    return 0;
}

易错点提示

  1. 自增自减运算符的优先级
   int i = 5;
   int j = i++ + ++i;  // 未定义行为!不同编译器结果不同
  1. 整数除法的陷阱
   int a = 5, b = 2;
   double c = a / b;  // c = 2.0,不是2.5!
   // 正确写法:
   double d = static_cast<double>(a) / b;  // d = 2.5
  1. 运算符优先级(从高到低部分):
  • :: 作用域
  • () [] . -> ++ --(后缀)
  • ++ --(前缀) + -(正负) ! ~ * &(取地址) sizeof
  • * / %
  • + -
  • << >>
  • < <= > >=
  • == !=
  • &(位与)
  • ^
  • |
  • &&
  • ||
  • ?:
  • = += -= 等赋值运算符
  • ,

1.6 输入输出

// 示例1.6:输入输出
#include <iostream>
#include <iomanip>  // 格式化输出
#include <string>   // 字符串类型
using namespace std;

int main() {
    // 1. 标准输出
    cout << "Hello, ";
    cout << "World!" << endl;  // endl会刷新缓冲区

    // 2. 标准输入
    int age;
    string name;

    cout << "Enter your name: ";
    // cin >> name;  // 遇到空格会停止
    getline(cin, name);  // 读取整行,包括空格

    cout << "Enter your age: ";
    cin >> age;

    // 注意:混合使用>>和getline时,需要清除缓冲区
    // 因为在输入年龄后按回车,换行符还在缓冲区
    // getline会读取到空行
    cin.ignore();  // 忽略一个字符(换行符)

    cout << "Name: " << name << ", Age: " << age << endl;

    // 3. 格式化输出
    double pi = 3.141592653589793;

    cout << fixed << setprecision(2);  // 固定小数位数,保留2位
    cout << "pi = " << pi << endl;     // 输出: 3.14

    cout << scientific;  // 科学计数法
    cout << "pi = " << pi << endl;  // 输出: 3.14e+00

    // 设置宽度和对齐
    cout << setw(10) << left << "Name" 
         << setw(10) << right << "Age" << endl;
    cout << setw(10) << left << "Alice" 
         << setw(10) << right << 25 << endl;
    cout << setw(10) << left << "Bob" 
         << setw(10) << right << 30 << endl;

    // 4. 错误输出
    cerr << "This is an error message!" << endl;

    return 0;
}

易错点提示

  1. cin和getline混合使用的坑
   int n;
   string s;

   cin >> n;        // 输入: 10[回车]
   getline(cin, s); // 会立即读取到空字符串!

   // 解决方法:
   cin >> n;
   cin.ignore();    // 忽略换行符
   getline(cin, s);
  1. 输入类型不匹配
   int num;
   cin >> num;  // 如果输入"abc",会失败但不会报错

   if (cin.fail()) {
       cin.clear();  // 清除错误状态
       cin.ignore(1000, '\n');  // 忽略错误输入
   }

综合例题与解析

// 例题1:类型转换的陷阱
#include <iostream>
using namespace std;

int main() {
    // 题目:下面代码的输出是什么?
    unsigned int a = 10;
    int b = -20;

    if (a + b > 0) {
        cout << "a + b > 0" << endl;
    } else {
        cout << "a + b <= 0" << endl;
    }

    cout << "a + b = " << a + b << endl;

    return 0;
}
/*
解析:
当有符号和无符号数混合运算时,有符号数会被转换为无符号数。
b = -20,转换为无符号数后变成很大的正数(2^32 - 20)
所以 a + b = 10 + (2^32 - 20) = 2^32 - 10 > 0
输出:
a + b > 0
a + b = 4294967286
*/
// 例题2:运算符优先级
#include <iostream>
using namespace std;

int main() {
    int a = 5, b = 3, c = 2;

    // 题目:下面表达式的值是多少?
    int result1 = a + b * c;      // 1. 5 + (3 * 2) = 11
    int result2 = a = b = c;      // 2. 从右向左赋值,a = b = c = 2
    bool result3 = a == b == c;   // 3. (a == b) == c → true == 2 → false

    cout << "result1 = " << result1 << endl;
    cout << "result2 = " << result2 << endl;
    cout << "result3 = " << result3 << endl;

    // 更复杂的例子
    int x = 1, y = 2, z = 3;
    x += y += z;  // 等价于: y = y + z; x = x + y;
    cout << "x = " << x << ", y = " << y << endl;

    return 0;
}
// 例题3:自增自减的陷阱
#include <iostream>
using namespace std;

int main() {
    int i = 5;

    // 题目:下面代码的输出是什么?
    cout << i++ << endl;   // 输出5,然后i=6
    cout << ++i << endl;   // i先变为7,输出7
    cout << i-- << endl;   // 输出7,然后i=6
    cout << --i << endl;   // i先变为5,输出5

    // 更复杂的例子(考试常见)
    int a = 5;
    int b = a++ + ++a;  // 未定义行为!避免这样写

    // 正确的理解方式应该是:
    // 表达式求值顺序不确定,不同编译器结果不同
    // GCC可能:a++返回5(a=6),++a返回7(a=7),b=12
    // 其他编译器可能不同

    return 0;
}

本章小结

  1. 基础语法:掌握程序结构、注释、标识符命名规则
  2. 数据类型:理解各类型的大小、范围,特别注意整数溢出和浮点数精度
  3. 类型转换:区分隐式和显式转换,避免signed/unsigned混合运算
  4. 变量与常量:理解作用域、生命周期,掌握const的各种用法
  5. 运算符:牢记优先级和结合性,避免复杂表达式中的未定义行为
  6. 输入输出:掌握格式化输出,注意输入时的缓冲区问题

第2章:流程控制

├── 2.1 选择结构
│   ├── if语句
│   ├── if-else语句
│   ├── if-else if-else语句
│   ├── switch语句
│   └── 嵌套选择结构
├── 2.2 循环结构
│   ├── while循环
│   ├── do-while循环
│   ├── for循环
│   ├── 范围for循环(C++11)
│   └── 循环控制语句
│       ├── break
│       ├── continue
│       └── goto(了解)
└── 2.3 跳转语句
    └── return语句

2.1 选择结构

2.1.1 if语句

// 示例2.1:基本if语句
#include <iostream>
using namespace std;

int main() {
    int score;
    cout << "请输入成绩:";
    cin >> score;

    // 1. 基本if语句
    if (score >= 60) {
        cout << "及格" << endl;
    }

    // 2. if-else语句
    if (score >= 60) {
        cout << "及格" << endl;
    } else {
        cout << "不及格" << endl;
    }

    // 3. if-else if-else语句
    if (score >= 90) {
        cout << "优秀" << endl;
    } else if (score >= 80) {
        cout << "良好" << endl;
    } else if (score >= 70) {
        cout << "中等" << endl;
    } else if (score >= 60) {
        cout << "及格" << endl;
    } else {
        cout << "不及格" << endl;
    }

    // 4. 嵌套if语句
    if (score >= 60) {
        if (score >= 90) {
            cout << "你很优秀!" << endl;
        }
        cout << "通过了考试" << endl;
    }

    return 0;
}

易错点提示

  1. 常见错误:将 == 误写为 =
   int a = 5;
   // 错误写法
   if (a = 10) {  // 总是为真,因为赋值表达式返回10
       cout << "a等于10" << endl;
   }
   // 正确写法
   if (a == 10) {
       cout << "a等于10" << endl;
   }
  1. else悬空问题
   int x = 10, y = 20;
   // 代码1
   if (x > 5)
       if (y > 15)
           cout << "条件1" << endl;
   else
       cout << "条件2" << endl;  // 这个else属于哪个if?

   // 等价于
   if (x > 5) {
       if (y > 15) {
           cout << "条件1" << endl;
       } else {
           cout << "条件2" << endl;  // 属于内层if
       }
   }

   // 建议:总是使用大括号
   if (x > 5) {
       if (y > 15) {
           cout << "条件1" << endl;
       }
   } else {
       cout << "条件2" << endl;
   }

2.1.2 条件运算符(三目运算符)

// 示例2.2:条件运算符
#include <iostream>
using namespace std;

int main() {
    int a = 10, b = 20;

    // 基本用法
    int max = (a > b) ? a : b;
    cout << "最大值是:" << max << endl;

    // 嵌套使用(可读性差,慎用)
    int c = 30;
    int max3 = (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);
    cout << "三个数的最大值是:" << max3 << endl;

    // 作为右值
    bool result = (a > b) ? true : false;
    cout << "a > b 吗?" << (result ? "是" : "否") << endl;

    // 注意:返回类型
    // 条件运算符的返回类型由两个操作数的类型决定
    int x = 10;
    double y = 3.14;
    auto z = (x > 5) ? x : y;  // z的类型是double
    cout << "z的类型:" << typeid(z).name() << endl;

    return 0;
}

2.1.3 switch语句

// 示例2.3:switch语句
#include <iostream>
using namespace std;

int main() {
    int day;
    cout << "请输入星期几(1-7):";
    cin >> day;

    switch (day) {
        case 1:
            cout << "星期一" << endl;
            break;  // 必须使用break,否则会继续执行下一个case
        case 2:
            cout << "星期二" << endl;
            break;
        case 3:
            cout << "星期三" << endl;
            break;
        case 4:
            cout << "星期四" << endl;
            break;
        case 5:
            cout << "星期五" << endl;
            break;
        case 6:
            cout << "星期六" << endl;
            break;
        case 7:
            cout << "星期日" << endl;
            break;
        default:  // 可选,处理其他情况
            cout << "输入错误!" << endl;
            break;
    }

    // switch的高级用法:多个case共享代码
    char grade;
    cout << "请输入等级(A-D):";
    cin >> grade;

    switch (grade) {
        case 'A':
        case 'a':
            cout << "优秀" << endl;
            break;
        case 'B':
        case 'b':
            cout << "良好" << endl;
            break;
        case 'C':
        case 'c':
            cout << "及格" << endl;
            break;
        case 'D':
        case 'd':
            cout << "不及格" << endl;
            break;
        default:
            cout << "无效等级" << endl;
    }

    return 0;
}

易错点提示

  1. break的重要性
   int x = 2;
   switch (x) {
       case 1:
           cout << "1" << endl;
       case 2:
           cout << "2" << endl;  // 从这里开始执行
       case 3:
           cout << "3" << endl;  // 继续执行
       default:
           cout << "default" << endl;  // 继续执行
   }
   // 输出:2 3 default(因为没有break)
  1. switch的限制
  • case值必须是整型常量表达式(整数、字符、枚举)
  • 不能使用浮点数、字符串作为case值
   // 错误示例
   double d = 1.0;
   switch (d) {  // 错误!必须是整型
       case 1.0:  // 错误!必须是整型常量
           // ...
   }

2.2 循环结构

2.2.1 while循环

// 示例2.4:while循环
#include <iostream>
using namespace std;

int main() {
    // 1. 基本while循环
    int i = 1;
    while (i <= 5) {
        cout << i << " ";
        i++;
    }
    cout << endl;

    // 2. 计算1到100的和
    int sum = 0, count = 1;
    while (count <= 100) {
        sum += count;
        count++;
    }
    cout << "1到100的和是:" << sum << endl;

    // 3. 输入验证循环
    int score;
    cout << "请输入成绩(0-100):";
    cin >> score;

    while (score < 0 || score > 100) {
        cout << "输入错误!请重新输入(0-100):";
        cin >> score;
    }
    cout << "输入的成绩是:" << score << endl;

    // 4. 无限循环(慎用)
    // while (true) {
    //     // 循环体
    //     // 必须有退出条件,否则死循环
    // }

    return 0;
}

2.2.2 do-while循环

// 示例2.5:do-while循环
#include <iostream>
using namespace std;

int main() {
    // 1. 基本do-while循环
    int i = 1;
    do {
        cout << i << " ";
        i++;
    } while (i <= 5);
    cout << endl;

    // 2. do-while至少执行一次
    int x = 10;
    do {
        cout << "x = " << x << endl;  // 会执行一次
        x++;
    } while (x < 5);

    // 3. 菜单选择示例
    char choice;
    do {
        cout << "\n===== 菜单 =====\n";
        cout << "1. 选项一\n";
        cout << "2. 选项二\n";
        cout << "3. 退出\n";
        cout << "请选择:";
        cin >> choice;

        switch (choice) {
            case '1':
                cout << "执行选项一" << endl;
                break;
            case '2':
                cout << "执行选项二" << endl;
                break;
            case '3':
                cout << "退出程序" << endl;
                break;
            default:
                cout << "无效选择,请重新输入" << endl;
        }
    } while (choice != '3');

    return 0;
}

易错点提示

  1. while与do-while的区别
   int i = 10;

   // while循环:先判断条件
   while (i < 5) {
       cout << "while循环" << endl;  // 不会执行
   }

   // do-while循环:先执行一次再判断条件
   do {
       cout << "do-while循环" << endl;  // 会执行一次
   } while (i < 5);

2.2.3 for循环

// 示例2.6:for循环
#include <iostream>
#include <vector>
using namespace std;

int main() {
    // 1. 基本for循环
    for (int i = 1; i <= 5; i++) {
        cout << i << " ";
    }
    cout << endl;

    // 2. 灵活的for循环
    int j = 1;
    for (; j <= 5; ) {  // 省略初始化和迭代部分
        cout << j << " ";
        j++;
    }
    cout << endl;

    // 3. 多个控制变量
    for (int i = 0, j = 10; i < j; i++, j--) {
        cout << "i=" << i << ", j=" << j << endl;
    }

    // 4. 无限循环
    // for (;;) {
    //     // 循环体
    //     // 需要break退出
    // }

    // 5. 计算阶乘
    int n = 5;
    long long factorial = 1;
    for (int i = 1; i <= n; i++) {
        factorial *= i;
    }
    cout << n << "! = " << factorial << endl;

    // 6. 循环嵌套(打印乘法表)
    cout << "\n乘法表:" << endl;
    for (int i = 1; i <= 9; i++) {
        for (int j = 1; j <= i; j++) {
            cout << j << "×" << i << "=" << i * j << "\t";
        }
        cout << endl;
    }

    return 0;
}

2.2.4 范围for循环(C++11)

// 示例2.7:范围for循环
#include <iostream>
#include <vector>
#include <array>
using namespace std;

int main() {
    // 1. 遍历数组
    int arr[] = {1, 2, 3, 4, 5};

    // 传统for循环
    cout << "传统for循环:";
    for (int i = 0; i < 5; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;

    // 范围for循环
    cout << "范围for循环:";
    for (int num : arr) {  // 遍历数组的每个元素
        cout << num << " ";
    }
    cout << endl;

    // 2. 使用auto自动推断类型
    cout << "使用auto:";
    for (auto num : arr) {
        cout << num << " ";
    }
    cout << endl;

    // 3. 使用引用修改元素
    cout << "修改前:";
    for (auto num : arr) {
        cout << num << " ";
    }
    cout << endl;

    for (auto &num : arr) {  // 使用引用
        num *= 2;  // 每个元素乘以2
    }

    cout << "修改后:";
    for (auto num : arr) {
        cout << num << " ";
    }
    cout << endl;

    // 4. 遍历vector
    vector<int> vec = {10, 20, 30, 40, 50};
    cout << "vector元素:";
    for (int val : vec) {
        cout << val << " ";
    }
    cout << endl;

    // 5. 遍历string
    string str = "Hello";
    cout << "字符串字符:";
    for (char ch : str) {
        cout << ch << " ";
    }
    cout << endl;

    return 0;
}

2.3 循环控制语句

2.3.1 break语句

// 示例2.8:break语句
#include <iostream>
using namespace std;

int main() {
    // 1. break在循环中的使用
    cout << "break示例:" << endl;
    for (int i = 1; i <= 10; i++) {
        if (i == 5) {
            break;  // 跳出整个循环
        }
        cout << i << " ";
    }
    cout << "\n循环结束" << endl;

    // 2. break在switch中的使用(前面已讲)

    // 3. break只能跳出一层循环
    cout << "\n嵌套循环中的break:" << endl;
    for (int i = 1; i <= 3; i++) {
        for (int j = 1; j <= 3; j++) {
            if (j == 2) {
                break;  // 只跳出内层循环
            }
            cout << "i=" << i << ", j=" << j << endl;
        }
    }

    // 4. 使用flag跳出多层循环
    cout << "\n跳出多层循环:" << endl;
    bool shouldBreak = false;
    for (int i = 1; i <= 3 && !shouldBreak; i++) {
        for (int j = 1; j <= 3; j++) {
            if (i == 2 && j == 2) {
                shouldBreak = true;
                break;  // 跳出内层循环
            }
            cout << "i=" << i << ", j=" << j << endl;
        }
    }

    return 0;
}

2.3.2 continue语句

// 示例2.9:continue语句
#include <iostream>
using namespace std;

int main() {
    // 1. continue的基本使用
    cout << "continue示例(跳过奇数):" << endl;
    for (int i = 1; i <= 10; i++) {
        if (i % 2 != 0) {  // 如果是奇数
            continue;      // 跳过本次循环剩余部分
        }
        cout << i << " ";  // 只输出偶数
    }
    cout << endl;

    // 2. continue与while循环
    cout << "while循环中的continue:" << endl;
    int i = 0;
    while (i < 10) {
        i++;
        if (i % 3 == 0) {
            continue;  // 跳过能被3整除的数
        }
        cout << i << " ";
    }
    cout << endl;

    // 3. 注意无限循环陷阱
    cout << "注意:错误的continue使用可能导致死循环" << endl;
    int j = 0;
    while (j < 10) {
        if (j % 2 == 0) {
            continue;  // 死循环!j永远不会增加
        }
        cout << j << " ";
        j++;  // 这行永远不会执行到
    }

    return 0;
}

2.3.3 goto语句(了解即可)

// 示例2.10:goto语句(一般不推荐使用)
#include <iostream>
using namespace std;

int main() {
    // goto语句:无条件跳转
    int i = 0;

loop:  // 标签
    if (i >= 5) {
        goto end;  // 跳转到end标签
    }
    cout << i << " ";
    i++;
    goto loop;  // 跳转到loop标签

end:  // 标签
    cout << "\n循环结束" << endl;

    // goto的合理使用场景:跳出多层嵌套
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            for (int k = 0; k < 3; k++) {
                if (i == 1 && j == 1 && k == 1) {
                    goto exit_all;  // 一次性跳出三层循环
                }
                cout << i << j << k << " ";
            }
        }
    }

exit_all:
    cout << "\n已跳出所有循环" << endl;

    return 0;
}

重要提示:goto语句会破坏程序的结构,使代码难以理解和维护。现代编程中应尽量避免使用goto,可以使用函数、break、return等替代。

综合例题与解析

// 例题1:选择结构嵌套
#include <iostream>
using namespace std;

int main() {
    int year;
    cout << "请输入年份:";
    cin >> year;

    // 判断闰年
    // 闰年条件:能被4整除但不能被100整除,或者能被400整除
    if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
        cout << year << "年是闰年" << endl;
    } else {
        cout << year << "年不是闰年" << endl;
    }

    // 等价的三目运算符写法
    string result = ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) 
                    ? "是闰年" : "不是闰年";
    cout << year << "年" << result << endl;

    return 0;
}
// 例题2:循环与选择结构结合
#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    // 统计各种成绩的人数
    int score;
    int countA = 0, countB = 0, countC = 0, countD = 0, countF = 0;
    int total = 0;

    cout << "请输入成绩(输入-1结束):" << endl;

    while (true) {
        cout << "成绩 " << total + 1 << ": ";
        cin >> score;

        if (score == -1) {
            break;  // 输入-1结束
        }

        if (score < 0 || score > 100) {
            cout << "成绩无效,请重新输入" << endl;
            continue;  // 跳过本次循环
        }

        // 统计成绩分布
        if (score >= 90) {
            countA++;
        } else if (score >= 80) {
            countB++;
        } else if (score >= 70) {
            countC++;
        } else if (score >= 60) {
            countD++;
        } else {
            countF++;
        }

        total++;
    }

    // 输出统计结果
    cout << "\n====== 成绩统计 ======" << endl;
    cout << "总人数: " << total << endl;
    cout << "A(90-100): " << countA << endl;
    cout << "B(80-89): " << countB << endl;
    cout << "C(70-79): " << countC << endl;
    cout << "D(60-69): " << countD << endl;
    cout << "F(0-59): " << countF << endl;

    // 计算百分比
    if (total > 0) {
        cout << fixed << setprecision(2);
        cout << "\n====== 百分比分布 ======" << endl;
        cout << "A: " << (countA * 100.0 / total) << "%" << endl;
        cout << "B: " << (countB * 100.0 / total) << "%" << endl;
        cout << "C: " << (countC * 100.0 / total) << "%" << endl;
        cout << "D: " << (countD * 100.0 / total) << "%" << endl;
        cout << "F: " << (countF * 100.0 / total) << "%" << endl;
    }

    return 0;
}
// 例题3:循环嵌套(打印图形)
#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "请输入行数:";
    cin >> n;

    // 打印直角三角形
    cout << "\n直角三角形:" << endl;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= i; j++) {
            cout << "* ";
        }
        cout << endl;
    }

    // 打印等腰三角形
    cout << "\n等腰三角形:" << endl;
    for (int i = 1; i <= n; i++) {
        // 打印空格
        for (int j = 1; j <= n - i; j++) {
            cout << " ";
        }
        // 打印星号
        for (int j = 1; j <= 2 * i - 1; j++) {
            cout << "*";
        }
        cout << endl;
    }

    // 打印菱形
    cout << "\n菱形:" << endl;
    // 上半部分
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n - i; j++) {
            cout << " ";
        }
        for (int j = 1; j <= 2 * i - 1; j++) {
            cout << "*";
        }
        cout << endl;
    }
    // 下半部分
    for (int i = n - 1; i >= 1; i--) {
        for (int j = 1; j <= n - i; j++) {
            cout << " ";
        }
        for (int j = 1; j <= 2 * i - 1; j++) {
            cout << "*";
        }
        cout << endl;
    }

    return 0;
}
// 例题4:素数判断(综合应用)
#include <iostream>
#include <cmath>
using namespace std;

int main() {
    // 方法1:判断单个数字是否为素数
    int num;
    cout << "请输入一个正整数:";
    cin >> num;

    if (num <= 1) {
        cout << num << "不是素数" << endl;
    } else {
        bool isPrime = true;

        // 优化:只需要检查到sqrt(num)
        for (int i = 2; i <= sqrt(num); i++) {
            if (num % i == 0) {
                isPrime = false;
                break;
            }
        }

        if (isPrime) {
            cout << num << "是素数" << endl;
        } else {
            cout << num << "不是素数" << endl;
        }
    }

    // 方法2:输出1-100之间的所有素数
    cout << "\n1-100之间的素数:" << endl;
    int count = 0;
    for (int i = 2; i <= 100; i++) {
        bool isPrime = true;

        // 检查i是否为素数
        for (int j = 2; j <= sqrt(i); j++) {
            if (i % j == 0) {
                isPrime = false;
                break;
            }
        }

        if (isPrime) {
            cout << i << " ";
            count++;
            if (count % 10 == 0) {  // 每10个换行
                cout << endl;
            }
        }
    }
    cout << "\n共有" << count << "个素数" << endl;

    return 0;
}

本章小结

重点回顾

  1. 选择结构:if、if-else、switch,注意break在switch中的使用
  2. 循环结构:while、do-while、for、范围for,理解各自适用场景
  3. 循环控制:break(跳出循环)、continue(跳过本次循环)、goto(了解即可)
  4. 嵌套结构:循环嵌套、选择与循环嵌套,注意代码可读性

易错易混淆点

  1. if条件中的赋值if (a = b) vs if (a == b)
  2. switch中的break:忘记break会导致穿透执行
  3. while与do-while:至少执行一次的区别
  4. 循环变量的作用域:for循环中定义的变量只在该循环内有效
  5. 无限循环陷阱:while循环中使用continue可能导致死循环
  6. break的作用范围:只能跳出当前一层循环

3. 复合数据类型

├── 3.1 数组
│   ├── 一维数组
│   ├── 多维数组
│   ├── 字符数组(C风格字符串)
│   └── 数组作为函数参数
├── 3.2 结构体(struct)
│   ├── 结构体定义与使用
│   ├── 结构体数组
│   ├── 结构体指针
│   └── 结构体嵌套
├── 3.3 共用体(union)
│   └── 匿名共用体
├── 3.4 枚举(enum)
│   ├── 传统枚举
│   └── 强类型枚举(C++11)
└── 3.5 类型推导
    ├── auto关键字(C++11)
    └── decltype关键字(C++11)

3.1 数组

3.1.1 一维数组

// 示例3.1:一维数组
#include <iostream>
#include <algorithm>  // 用于sort函数
using namespace std;

int main() {
    // 1. 数组的声明和初始化
    int arr1[5];  // 声明一个包含5个整数的数组(未初始化)

    // 初始化方式
    int arr2[5] = {1, 2, 3, 4, 5};  // 完全初始化
    int arr3[] = {1, 2, 3, 4, 5};   // 自动推断大小
    int arr4[5] = {1, 2};           // 部分初始化,其余为0
    int arr5[5] = {0};              // 全部初始化为0

    // C++11统一初始化语法
    int arr6[]{1, 2, 3, 4, 5};
    int arr7[5]{1, 2, 3, 4, 5};

    // 2. 访问数组元素
    cout << "arr2的元素:" << endl;
    for (int i = 0; i < 5; i++) {
        cout << "arr2[" << i << "] = " << arr2[i] << endl;
    }

    // 3. 数组大小计算
    int arr8[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int size = sizeof(arr8) / sizeof(arr8[0]);  // 计算元素个数
    cout << "arr8的大小: " << size << endl;

    // 4. 数组与指针的关系(重要!)
    // 数组名在大多数情况下会被转换为指向第一个元素的指针
    cout << "\n数组名和指针的关系:" << endl;
    cout << "arr8 = " << arr8 << endl;          // 数组首地址
    cout << "&arr8[0] = " << &arr8[0] << endl;  // 第一个元素的地址
    cout << "*arr8 = " << *arr8 << endl;        // 第一个元素的值

    // 5. 数组遍历的几种方式
    cout << "\n数组遍历方式:" << endl;

    // 方式1:下标遍历
    cout << "下标遍历:";
    for (int i = 0; i < size; i++) {
        cout << arr8[i] << " ";
    }
    cout << endl;

    // 方式2:指针遍历
    cout << "指针遍历:";
    for (int* p = arr8; p < arr8 + size; p++) {
        cout << *p << " ";
    }
    cout << endl;

    // 方式3:范围for循环(C++11)
    cout << "范围for循环:";
    for (int num : arr8) {
        cout << num << " ";
    }
    cout << endl;

    // 6. 数组的常见操作
    int scores[10] = {85, 90, 78, 92, 88, 76, 95, 81, 89, 73};

    // 求和
    int sum = 0;
    for (int i = 0; i < 10; i++) {
        sum += scores[i];
    }
    cout << "平均分: " << sum / 10.0 << endl;

    // 找最大值
    int max_score = scores[0];
    for (int i = 1; i < 10; i++) {
        if (scores[i] > max_score) {
            max_score = scores[i];
        }
    }
    cout << "最高分: " << max_score << endl;

    // 排序(冒泡排序)
    int nums[] = {5, 3, 8, 1, 9, 2, 7, 4, 6};
    int n = sizeof(nums) / sizeof(nums[0]);

    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (nums[j] > nums[j + 1]) {
                // 交换
                int temp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = temp;
            }
        }
    }

    cout << "排序后:";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}

易错点提示

  1. 数组越界访问
   int arr[5] = {1, 2, 3, 4, 5};
   cout << arr[5];  // 错误!索引0-4,arr[5]越界
   // 越界访问可能不报错,但结果是未定义的,可能访问到其他内存
  1. 数组不能直接赋值
   int a[3] = {1, 2, 3};
   int b[3];
   // b = a;  // 错误!数组不能直接赋值
   // 必须逐个元素复制
   for (int i = 0; i < 3; i++) {
       b[i] = a[i];
   }
  1. 数组大小必须是编译时常量
   int n;
   cin >> n;
   // int arr[n];  // 错误!C++标准不支持变长数组(但某些编译器扩展支持)
   // 正确做法:使用动态内存分配或vector

3.1.2 多维数组

// 示例3.2:多维数组
#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    // 1. 二维数组的声明和初始化
    // 方式1:指定所有维度
    int matrix1[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    // 方式2:省略第一维(编译器可以推断)
    int matrix2[][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    // 方式3:线性初始化(按内存顺序)
    int matrix3[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

    // 2. 访问二维数组元素
    cout << "二维数组元素:" << endl;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            cout << setw(3) << matrix1[i][j] << " ";
        }
        cout << endl;
    }

    // 3. 计算行数和列数
    int rows = sizeof(matrix1) / sizeof(matrix1[0]);
    int cols = sizeof(matrix1[0]) / sizeof(matrix1[0][0]);
    cout << "\n矩阵大小: " << rows << "行 × " << cols << "列" << endl;

    // 4. 二维数组在内存中的布局
    // 二维数组实际上是"数组的数组",在内存中是连续存储的
    cout << "\n内存地址验证:" << endl;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            cout << "&matrix1[" << i << "][" << j << "] = " 
                 << &matrix1[i][j] << endl;
        }
    }

    // 5. 实用示例:矩阵运算
    int A[2][3] = {
        {1, 2, 3},
        {4, 5, 6}
    };

    int B[2][3] = {
        {6, 5, 4},
        {3, 2, 1}
    };

    int C[2][3];  // 结果矩阵

    // 矩阵相加
    cout << "\n矩阵加法:" << endl;
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            C[i][j] = A[i][j] + B[i][j];
            cout << setw(3) << C[i][j] << " ";
        }
        cout << endl;
    }

    // 6. 三维数组
    int cube[2][3][4] = {
        {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12}
        },
        {
            {13, 14, 15, 16},
            {17, 18, 19, 20},
            {21, 22, 23, 24}
        }
    };

    cout << "\n三维数组访问:" << endl;
    for (int i = 0; i < 2; i++) {
        cout << "第" << i << "层:" << endl;
        for (int j = 0; j < 3; j++) {
            for (int k = 0; k < 4; k++) {
                cout << setw(3) << cube[i][j][k] << " ";
            }
            cout << endl;
        }
        cout << endl;
    }

    return 0;
}

易错点提示

  1. 多维数组初始化必须指定除第一维外的所有维度
   // int arr[][] = {{1,2}, {3,4}};  // 错误!必须指定列数
   int arr[][2] = {{1,2}, {3,4}};    // 正确
  1. 二维数组作为函数参数的特殊语法
   // 错误声明
   // void func(int arr[][]);  

   // 正确声明(必须指定列数)
   void func(int arr[][4]);

   // 或者使用指针
   void func(int (*arr)[4]);

3.1.3 字符数组和C风格字符串

// 示例3.3:字符数组和C风格字符串
#include <iostream>
#include <cstring>  // C风格字符串函数
using namespace std;

int main() {
    // 1. 字符数组的不同初始化方式
    char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'};  // 手动添加\0
    char str2[] = {'H', 'e', 'l', 'l', 'o', '\0'};   // 自动计算大小
    char str3[] = "Hello";  // 字符串字面量,自动添加\0
    char str4[10] = "Hello";  // 指定大小,剩余部分用\0填充

    // 2. C风格字符串的输出
    cout << "str1: " << str1 << endl;
    cout << "str3: " << str3 << endl;

    // 3. C风格字符串的长度
    cout << "\n字符串长度(不包括\\0):" << endl;
    cout << "strlen(str3) = " << strlen(str3) << endl;  // 5
    cout << "sizeof(str3) = " << sizeof(str3) << endl;  // 6(包含\0)

    // 4. 字符串操作函数
    char src[20] = "Hello";
    char dest[20];

    // 复制字符串
    strcpy(dest, src);
    cout << "\nstrcpy后,dest: " << dest << endl;

    // 连接字符串
    strcat(dest, " World!");
    cout << "strcat后,dest: " << dest << endl;

    // 比较字符串
    char s1[] = "apple";
    char s2[] = "banana";
    int cmp = strcmp(s1, s2);
    if (cmp < 0) {
        cout << "\"" << s1 << "\" < \"" << s2 << "\"" << endl;
    } else if (cmp > 0) {
        cout << "\"" << s1 << "\" > \"" << s2 << "\"" << endl;
    } else {
        cout << "字符串相等" << endl;
    }

    // 5. 字符串查找
    char text[] = "This is a test string.";
    char *found = strstr(text, "test");
    if (found != nullptr) {
        cout << "\n找到子串,位置: " << (found - text) << endl;
        cout << "子串内容: " << found << endl;
    }

    // 6. 字符串输入
    char name[50];
    cout << "\n请输入你的名字(最多49个字符): ";

    // cin >> name;  // 遇到空格会停止
    cin.getline(name, 50);  // 读取整行,包括空格
    cout << "你好, " << name << "!" << endl;

    // 7. 常见错误:缓冲区溢出
    char small[5] = "Test";
    // strcat(small, " Too Long");  // 危险!缓冲区溢出

    // 安全版本:使用strn系列函数
    char safe[10] = "Hello";
    strncat(safe, " World!", sizeof(safe) - strlen(safe) - 1);
    cout << "\n安全连接: " << safe << endl;

    // 8. 字符数组与字符串字面量
    char arr[] = "C++";     // 可以修改
    // arr[0] = 'c';         // 正确,可以修改

    const char* ptr = "C++"; // 字符串字面量在只读内存
    // ptr[0] = 'c';        // 错误!试图修改只读内存

    // 正确:使用const char*指向字符串字面量
    const char* greeting = "Hello, World!";
    cout << greeting << endl;

    return 0;
}

重要概念

  1. 字符串字面量:如”Hello”是常量,存储在只读内存区
  2. ‘\0’:空字符,ASCII值为0,表示字符串结束
  3. sizeof vs strlen
  • sizeof:数组的总大小(包含\0)
  • strlen:字符串的实际长度(不包括\0)

3.2 结构体(struct)

3.2.1 结构体的基本使用

// 示例3.4:结构体的基本使用
#include <iostream>
#include <cstring>
using namespace std;

// 1. 结构体定义
struct Student {
    // 成员变量
    char name[50];
    int id;
    int age;
    float score;

    // C++中结构体可以包含成员函数(与类的区别很小)
    void display() {
        cout << "姓名: " << name << endl;
        cout << "学号: " << id << endl;
        cout << "年龄: " << age << endl;
        cout << "成绩: " << score << endl;
    }

    void setScore(float newScore) {
        if (newScore >= 0 && newScore <= 100) {
            score = newScore;
        }
    }
};

// 也可以先声明后定义
struct Point;  // 前向声明

struct Point {
    int x;
    int y;
};

int main() {
    // 2. 结构体变量的声明和初始化
    // 方式1:逐个成员初始化
    Student stu1;
    strcpy(stu1.name, "张三");
    stu1.id = 1001;
    stu1.age = 20;
    stu1.score = 85.5;

    // 方式2:聚合初始化(C++11之前)
    Student stu2 = {"李四", 1002, 21, 90.0};

    // 方式3:C++11统一初始化
    Student stu3{"王五", 1003, 19, 88.5};

    // 方式4:指定成员初始化(C++20)
    // Student stu4{.name="赵六", .id=1004, .age=22, .score=92.0};

    // 3. 访问结构体成员
    cout << "学生信息:" << endl;
    cout << "姓名: " << stu1.name << endl;
    cout << "学号: " << stu1.id << endl;

    // 4. 使用成员函数
    cout << "\n使用成员函数显示信息:" << endl;
    stu1.display();

    // 5. 结构体赋值(逐字节复制)
    Student stu5 = stu1;  // 复制整个结构体
    stu5.id = 1005;
    cout << "\n复制后的学生:" << endl;
    stu5.display();

    // 6. 结构体数组
    Student class1[3] = {
        {"小明", 2001, 18, 78.5},
        {"小红", 2002, 19, 92.0},
        {"小刚", 2003, 20, 85.0}
    };

    cout << "\n班级学生信息:" << endl;
    for (int i = 0; i < 3; i++) {
        class1[i].display();
        cout << "---" << endl;
    }

    // 7. 结构体指针
    Student* pStu = &stu1;

    // 访问指针指向的结构体成员
    // 方式1:解引用然后使用点运算符
    (*pStu).age = 21;

    // 方式2:使用箭头运算符(更常用)
    pStu->score = 88.0;

    cout << "\n通过指针修改后:" << endl;
    stu1.display();

    // 8. 结构体嵌套
    struct Date {
        int year;
        int month;
        int day;
    };

    struct Employee {
        char name[50];
        int id;
        Date hireDate;  // 嵌套结构体
        double salary;
    };

    Employee emp = {
        "张经理",
        5001,
        {2020, 3, 15},  // 嵌套初始化
        8000.0
    };

    cout << "\n员工信息:" << endl;
    cout << "姓名: " << emp.name << endl;
    cout << "入职日期: " << emp.hireDate.year << "-" 
         << emp.hireDate.month << "-" << emp.hireDate.day << endl;

    // 9. 结构体大小和内存对齐
    cout << "\n结构体大小:" << endl;
    cout << "sizeof(Student) = " << sizeof(Student) << endl;
    cout << "sizeof(stu1) = " << sizeof(stu1) << endl;

    // 查看成员偏移量
    cout << "\n成员偏移量:" << endl;
    cout << "offsetof(Student, name) = " << offsetof(Student, name) << endl;
    cout << "offsetof(Student, id) = " << offsetof(Student, id) << endl;
    cout << "offsetof(Student, age) = " << offsetof(Student, age) << endl;
    cout << "offsetof(Student, score) = " << offsetof(Student, score) << endl;

    return 0;
}

重要概念

  1. 结构体与类的区别:在C++中,struct默认成员是public,class默认是private
  2. 内存对齐:为了提高访问效率,编译器会对结构体成员进行内存对齐
  3. 结构体赋值:支持直接赋值,是浅拷贝(逐字节复制)

3.2.2 结构体中的位字段

// 示例3.5:结构体位字段
#include <iostream>
using namespace std;

int main() {
    // 位字段:节省内存空间,用于硬件编程、网络协议等

    struct PackedData {
        // 冒号后的数字表示该字段占用的位数
        unsigned int flag1 : 1;   // 1位
        unsigned int flag2 : 1;   // 1位
        unsigned int type  : 4;   // 4位,可以表示0-15
        unsigned int value : 10;  // 10位,可以表示0-1023
        unsigned int : 0;         // 匿名位字段,强制对齐到下一个字边界
        unsigned int extra : 16;  // 16位
    };

    PackedData data;
    data.flag1 = 1;
    data.flag2 = 0;
    data.type = 5;
    data.value = 255;
    data.extra = 65535;

    cout << "位字段示例:" << endl;
    cout << "flag1: " << data.flag1 << endl;
    cout << "flag2: " << data.flag2 << endl;
    cout << "type: " << data.type << endl;
    cout << "value: " << data.value << endl;
    cout << "extra: " << data.extra << endl;

    cout << "\n结构体大小: " << sizeof(PackedData) << " bytes" << endl;

    // 注意:位字段的地址不可取
    // unsigned int* p = &data.flag1;  // 错误!

    return 0;
}

3.3 共用体(union)

// 示例3.6:共用体
#include <iostream>
#include <cstring>
using namespace std;

int main() {
    // 1. 共用体定义
    // 共用体的所有成员共享同一块内存
    union Data {
        int i;
        float f;
        char str[20];
    };

    // 2. 共用体变量的声明
    Data data;

    cout << "共用体大小: " << sizeof(data) << " bytes" << endl;

    // 3. 使用共用体
    data.i = 10;
    cout << "data.i = " << data.i << endl;

    data.f = 3.14f;
    cout << "data.f = " << data.f << endl;
    cout << "注意:data.i现在是未定义的: " << data.i << endl;

    strcpy(data.str, "Hello");
    cout << "data.str = " << data.str << endl;

    // 4. 匿名共用体(C++11)
    struct Variant {
        enum Type { INT, FLOAT, STRING } type;

        union {
            int intValue;
            float floatValue;
            char stringValue[20];
        };  // 匿名共用体,成员可以直接访问

        void setInt(int val) {
            type = INT;
            intValue = val;
        }

        void display() {
            switch (type) {
                case INT:
                    cout << "整数: " << intValue << endl;
                    break;
                case FLOAT:
                    cout << "浮点数: " << floatValue << endl;
                    break;
                case STRING:
                    cout << "字符串: " << stringValue << endl;
                    break;
            }
        }
    };

    Variant var;
    var.setInt(100);
    var.display();

    // 5. 共用体的实际应用:类型转换
    union Converter {
        int i;
        float f;

        float intToFloat(int val) {
            i = val;
            return f;
        }

        int floatToInt(float val) {
            f = val;
            return i;
        }
    };

    Converter conv;
    int intVal = 1092616192;  // 对应的float是10.5
    float floatVal = conv.intToFloat(intVal);
    cout << "\n类型转换:" << endl;
    cout << "整数 " << intVal << " 对应的浮点数: " << floatVal << endl;

    // 6. 带构造函数的共用体(C++11)
    union ComplexUnion {
        int x;
        double y;
        char z;

        // C++11允许共用体有构造函数
        ComplexUnion() : x(0) {}  // 默认初始化x为0
    };

    ComplexUnion cu;
    cout << "初始化后 cu.x = " << cu.x << endl;

    return 0;
}

重要概念

  1. 共用体大小:等于最大成员的大小
  2. 内存共享:所有成员共享同一块内存,修改一个成员会影响其他成员
  3. 用途:节省内存、类型转换、实现变体类型

3.4 枚举(enum)

// 示例3.7:枚举
#include <iostream>
using namespace std;

int main() {
    // 1. 传统枚举(C风格)
    enum Color {
        RED,      // 0
        GREEN,    // 1
        BLUE,     // 2
        YELLOW = 10,  // 可以指定值
        PURPLE       // 11(前一个值+1)
    };

    Color c1 = RED;
    Color c2 = BLUE;

    cout << "传统枚举:" << endl;
    cout << "RED = " << RED << endl;
    cout << "GREEN = " << GREEN << endl;
    cout << "BLUE = " << BLUE << endl;
    cout << "YELLOW = " << YELLOW << endl;
    cout << "PURPLE = " << PURPLE << endl;

    // 枚举可以隐式转换为整数
    int colorValue = c1;
    cout << "c1的值: " << colorValue << endl;

    // 但整数不能隐式转换为枚举(需要强制转换)
    // Color c3 = 2;  // 错误!
    Color c3 = static_cast<Color>(2);  // 正确

    // 2. 强类型枚举(C++11)
    enum class Day {
        MONDAY,    // 0
        TUESDAY,   // 1
        WEDNESDAY, // 2
        THURSDAY,  // 3
        FRIDAY,    // 4
        SATURDAY,  // 5
        SUNDAY     // 6
    };

    enum class Month : unsigned char {  // 指定底层类型
        JAN = 1, FEB, MAR, APR, MAY, JUN,
        JUL, AUG, SEP, OCT, NOV, DEC
    };

    Day today = Day::MONDAY;
    Month thisMonth = Month::APR;

    cout << "\n强类型枚举:" << endl;

    // 必须使用作用域解析运算符
    // cout << today << endl;  // 错误!不能隐式转换

    // 可以显式转换
    cout << "today: " << static_cast<int>(today) << endl;
    cout << "thisMonth: " << static_cast<int>(thisMonth) << endl;

    // 3. 枚举的大小
    cout << "\n枚举大小:" << endl;
    cout << "sizeof(Color) = " << sizeof(Color) << endl;
    cout << "sizeof(Day) = " << sizeof(Day) << endl;
    cout << "sizeof(Month) = " << sizeof(Month) << endl;

    // 4. 枚举的switch用法
    cout << "\n枚举在switch中的使用:" << endl;
    switch (c1) {
        case RED:
            cout << "红色" << endl;
            break;
        case GREEN:
            cout << "绿色" << endl;
            break;
        case BLUE:
            cout << "蓝色" << endl;
            break;
        default:
            cout << "其他颜色" << endl;
    }

    // 5. 枚举与整数的比较
    Color myColor = GREEN;
    if (myColor == GREEN) {
        cout << "颜色是绿色" << endl;
    }

    // 6. 枚举的应用:状态机
    enum class State {
        IDLE,
        RUNNING,
        PAUSED,
        STOPPED
    };

    State currentState = State::IDLE;

    // 模拟状态转换
    currentState = State::RUNNING;
    cout << "\n当前状态: " << static_cast<int>(currentState) << endl;

    // 7. 遍历枚举值(需要额外处理)
    cout << "\n遍历Month枚举:" << endl;
    for (int i = static_cast<int>(Month::JAN); 
         i <= static_cast<int>(Month::DEC); 
         i++) {
        cout << i << " ";
    }
    cout << endl;

    return 0;
}

重要概念

  1. 传统枚举的缺点
  • 枚举值会污染外层命名空间
  • 可以隐式转换为整数
  • 可以与其他枚举类型比较(类型不安全)
  1. 强类型枚举的优点
  • 必须使用枚举类型::枚举值访问
  • 不会污染外层命名空间
  • 不能隐式转换为整数
  • 类型安全

3.5 类型推导(C++11)

// 示例3.8:类型推导
#include <iostream>
#include <vector>
#include <map>
#include <typeinfo>  // typeid运算符
using namespace std;

int main() {
    // 1. auto关键字(自动类型推断)
    auto x = 5;          // x的类型是int
    auto y = 3.14;       // y的类型是double
    auto z = "Hello";    // z的类型是const char*

    cout << "auto类型推导:" << endl;
    cout << "x的类型: " << typeid(x).name() << endl;
    cout << "y的类型: " << typeid(y).name() << endl;
    cout << "z的类型: " << typeid(z).name() << endl;

    // auto在循环中的应用
    vector<int> numbers = {1, 2, 3, 4, 5};

    cout << "\n传统for循环:" << endl;
    for (vector<int>::iterator it = numbers.begin(); 
         it != numbers.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    cout << "使用auto的for循环:" << endl;
    for (auto it = numbers.begin(); it != numbers.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    cout << "范围for循环 + auto:" << endl;
    for (auto num : numbers) {
        cout << num << " ";
    }
    cout << endl;

    // 2. decltype关键字(获取表达式的类型)
    int a = 10;
    decltype(a) b = 20;  // b的类型与a相同,即int

    const int c = 30;
    decltype(c) d = 40;  // d的类型是const int

    decltype(a + 3.14) e;  // e的类型是double

    cout << "\ndecltype示例:" << endl;
    cout << "sizeof(b) = " << sizeof(b) << endl;
    cout << "sizeof(e) = " << sizeof(e) << endl;

    // 3. auto和decltype的区别
    // auto会去掉引用和const修饰,decltype会保留
    int num = 10;
    int& ref = num;
    const int const_num = 20;

    auto auto1 = ref;          // auto1是int,去掉了引用
    decltype(ref) decl1 = num; // decl1是int&,保留了引用

    auto auto2 = const_num;    // auto2是int,去掉了const
    decltype(const_num) decl2 = 30; // decl2是const int

    // 4. 后置返回类型(trailing return type)
    // 使用auto和decltype结合,可以推导函数返回类型
    auto add(int x, int y) -> int {
        return x + y;
    }

    // 更复杂的例子:模板函数
    template<typename T, typename U>
    auto multiply(T x, U y) -> decltype(x * y) {
        return x * y;
    }

    // 实际使用
    auto result1 = add(3, 4);
    auto result2 = multiply(3, 4.5);  // 返回double

    cout << "add(3, 4) = " << result1 << endl;
    cout << "multiply(3, 4.5) = " << result2 << endl;

    // 5. 在复杂数据结构中使用auto
    map<string, vector<int>> data = {
        {"Alice", {85, 90, 88}},
        {"Bob", {78, 82, 80}},
        {"Charlie", {92, 95, 90}}
    };

    cout << "\n复杂数据结构遍历:" << endl;
    // 不使用auto(代码冗长)
    for (map<string, vector<int>>::iterator it = data.begin();
         it != data.end(); ++it) {
        cout << it->first << ": ";
        for (vector<int>::iterator vit = it->second.begin();
             vit != it->second.end(); ++vit) {
            cout << *vit << " ";
        }
        cout << endl;
    }

    // 使用auto(代码简洁)
    cout << "\n使用auto:" << endl;
    for (auto& entry : data) {
        cout << entry.first << ": ";
        for (auto score : entry.second) {
            cout << score << " ";
        }
        cout << endl;
    }

    // 6. 注意事项
    // auto不能用于函数参数(C++20之前)
    // void func(auto x) {}  // C++20允许

    // auto不能推导数组类型
    int arr[] = {1, 2, 3};
    auto arr_auto = arr;  // arr_auto是int*,不是int[3]

    // 使用decltype可以保留数组类型
    decltype(arr) arr_copy = {4, 5, 6};  // arr_copy是int[3]

    return 0;
}

重要概念

  1. auto推导规则
  • 忽略顶层const和引用
  • 数组退化为指针
  • 函数退化为函数指针
  1. decltype推导规则
  • 如果表达式是变量,则保留变量的类型(包括const和引用)
  • 如果表达式不是变量,则推导出表达式的类型
  1. auto vs decltype
  • 使用auto想要推断类型时
  • 使用decltype想要精确知道表达式的类型时

综合例题与解析

// 例题1:学生成绩管理系统
#include <iostream>
#include <cstring>
#include <iomanip>
using namespace std;

struct Student {
    int id;
    char name[20];
    float scores[3];  // 三门课程的成绩
    float average;
    char grade;       // 等级:A(>=90), B(>=80), C(>=70), D(>=60), F(<60)
};

void calculateGrade(Student& stu) {
    stu.average = (stu.scores[0] + stu.scores[1] + stu.scores[2]) / 3.0;

    if (stu.average >= 90) stu.grade = 'A';
    else if (stu.average >= 80) stu.grade = 'B';
    else if (stu.average >= 70) stu.grade = 'C';
    else if (stu.average >= 60) stu.grade = 'D';
    else stu.grade = 'F';
}

void displayStudent(const Student& stu) {
    cout << left << setw(6) << stu.id
         << setw(15) << stu.name
         << fixed << setprecision(1);

    for (int i = 0; i < 3; i++) {
        cout << setw(8) << stu.scores[i];
    }

    cout << setw(10) << stu.average
         << setw(6) << stu.grade << endl;
}

int main() {
    const int NUM_STUDENTS = 3;
    Student students[NUM_STUDENTS];

    // 输入学生信息
    for (int i = 0; i < NUM_STUDENTS; i++) {
        cout << "\n输入第" << i + 1 << "个学生的信息:" << endl;

        cout << "学号:";
        cin >> students[i].id;

        cout << "姓名:";
        cin >> students[i].name;

        cout << "三门课程的成绩:";
        for (int j = 0; j < 3; j++) {
            cin >> students[i].scores[j];
        }

        calculateGrade(students[i]);
    }

    // 显示学生信息
    cout << "\n===== 学生成绩单 =====" << endl;
    cout << left << setw(6) << "学号"
         << setw(15) << "姓名"
         << setw(8) << "成绩1"
         << setw(8) << "成绩2"
         << setw(8) << "成绩3"
         << setw(10) << "平均分"
         << setw(6) << "等级" << endl;
    cout << string(60, '-') << endl;

    for (int i = 0; i < NUM_STUDENTS; i++) {
        displayStudent(students[i]);
    }

    // 统计各等级人数
    int gradeCount[5] = {0};  // A,B,C,D,F

    for (int i = 0; i < NUM_STUDENTS; i++) {
        switch (students[i].grade) {
            case 'A': gradeCount[0]++; break;
            case 'B': gradeCount[1]++; break;
            case 'C': gradeCount[2]++; break;
            case 'D': gradeCount[3]++; break;
            case 'F': gradeCount[4]++; break;
        }
    }

    cout << "\n===== 等级统计 =====" << endl;
    char grades[] = {'A', 'B', 'C', 'D', 'F'};
    for (int i = 0; i < 5; i++) {
        cout << grades[i] << ": " << gradeCount[i] << "人" << endl;
    }

    return 0;
}
// 例题2:矩阵运算
#include <iostream>
#include <iomanip>
using namespace std;

const int ROWS = 3;
const int COLS = 3;

// 使用typedef定义矩阵类型
typedef int Matrix[ROWS][COLS];

void printMatrix(const Matrix mat, const string& name) {
    cout << "\n" << name << " = " << endl;
    for (int i = 0; i < ROWS; i++) {
        for (int j = 0; j < COLS; j++) {
            cout << setw(4) << mat[i][j];
        }
        cout << endl;
    }
}

void addMatrix(const Matrix a, const Matrix b, Matrix result) {
    for (int i = 0; i < ROWS; i++) {
        for (int j = 0; j < COLS; j++) {
            result[i][j] = a[i][j] + b[i][j];
        }
    }
}

void multiplyMatrix(const Matrix a, const Matrix b, Matrix result) {
    for (int i = 0; i < ROWS; i++) {
        for (int j = 0; j < COLS; j++) {
            result[i][j] = 0;
            for (int k = 0; k < COLS; k++) {
                result[i][j] += a[i][k] * b[k][j];
            }
        }
    }
}

void transposeMatrix(const Matrix mat, Matrix result) {
    for (int i = 0; i < ROWS; i++) {
        for (int j = 0; j < COLS; j++) {
            result[j][i] = mat[i][j];
        }
    }
}

int main() {
    Matrix A = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };

    Matrix B = {
        {9, 8, 7},
        {6, 5, 4},
        {3, 2, 1}
    };

    Matrix C;  // 用于存储结果

    printMatrix(A, "A");
    printMatrix(B, "B");

    // 矩阵加法
    addMatrix(A, B, C);
    printMatrix(C, "A + B");

    // 矩阵乘法
    multiplyMatrix(A, B, C);
    printMatrix(C, "A × B");

    // 矩阵转置
    transposeMatrix(A, C);
    printMatrix(C, "A的转置");

    return 0;
}
// 例题3:联合体的实际应用 - IP地址转换
#include <iostream>
#include <cstdint>  // 固定宽度整数类型
using namespace std;

// 使用联合体将32位IP地址分解为4个字节
union IPAddress {
    uint32_t address;  // 32位无符号整数

    struct {
        uint8_t octet4;  // 最高位字节
        uint8_t octet3;
        uint8_t octet2;
        uint8_t octet1;  // 最低位字节
    } octets;
};

int main() {
    IPAddress ip;

    // 设置IP地址为 192.168.1.100
    // 注意:网络字节序通常是大端序,这里假设是小端序系统
    ip.octets.octet1 = 100;  // 最后一位
    ip.octets.octet2 = 1;
    ip.octets.octet3 = 168;
    ip.octets.octet4 = 192;  // 第一位

    cout << "IP地址的32位值: " << ip.address << endl;
    cout << "IP地址点分十进制: "
         << (int)ip.octets.octet4 << "."
         << (int)ip.octets.octet3 << "."
         << (int)ip.octets.octet2 << "."
         << (int)ip.octets.octet1 << endl;

    // 另一种方式:直接设置32位值
    ip.address = 0xC0A80164;  // 192.168.1.100的十六进制

    cout << "\n直接设置32位值后:" << endl;
    cout << "IP地址点分十进制: "
         << (int)ip.octets.octet4 << "."
         << (int)ip.octets.octet3 << "."
         << (int)ip.octets.octet2 << "."
         << (int)ip.octets.octet1 << endl;

    return 0;
}

本章小结

重点回顾

  1. 数组:一维、多维数组的声明、初始化和使用,特别注意数组与指针的关系
  2. 结构体:自定义数据类型,包含成员变量和函数,理解内存对齐
  3. 共用体:所有成员共享内存,用于节省空间和类型转换
  4. 枚举:传统枚举和强类型枚举的区别和应用场景
  5. 类型推导:auto和decltype的使用场景和区别

易错易混淆点

  1. 数组越界:C++不检查数组边界,需要程序员自己保证
  2. 数组名与指针:数组名在大多数情况下会退化为指针,但sizeof(数组名)例外
  3. 结构体赋值:直接赋值是浅拷贝,对于指针成员需要特别小心
  4. 枚举作用域:传统枚举会污染命名空间,强类型枚举不会
  5. auto推导:会去掉顶层const和引用,decltype会保留

考研笔试常见题型

  1. 选择题:考察复合数据类型的基本概念和特性
  2. 程序阅读题:分析涉及数组、结构体的复杂程序输出
  3. 程序填空题:补全结构体定义、数组操作等代码
  4. 编程题:实现特定功能,如学生管理系统、矩阵运算等

编程实践建议

  1. 数组安全:总是检查数组边界,使用std::array或std::vector更安全
  2. 结构体设计:合理设计结构体,注意内存对齐对性能的影响
  3. 类型选择:根据需求选择合适的复合数据类型
  4. 代码可读性:为结构体和枚举使用有意义的名称
  5. 现代C++:优先使用C++11的特性,如范围for循环、auto、强类型枚举

学习检查

  1. 你能解释数组名在什么情况下不会退化为指针吗?
  2. 结构体和类在C++中的主要区别是什么?
  3. 什么情况下应该使用共用体?
  4. 为什么C++11要引入强类型枚举?
  5. auto和decltype的主要区别是什么?

1) 数组名在什么情况下不会退化为指针?

“数组退化(decay)”指:数组表达式在大多数场合会隐式转换成指向首元素的指针(T*)。不退化的典型场合主要有:

  1. sizeof 运算符
   int a[10];
   sizeof(a);      // 得到整个数组字节数,不是指针大小
  1. decltype
   int a[10];
   decltype(a) x = a;  // x 的类型是 int[10](数组类型)
  1. 取地址 &a
   int a[10];
   auto p = &a;   // p 的类型是 int (*)[10](指向“数组”的指针),不是 int*
  1. 用引用接收数组(不会退化成指针)
   template <size_t N>
   void f(int (&arr)[N]) { /* arr 是数组引用 */ }

   int a[10];
   f(a); // N=10,没退化
  1. 范围 for(range-based for)遍历数组
    这里编译器用数组的边界信息来生成遍历逻辑,并不把它当成普通 int* 参数传进去(内部会用 begin/end 语义)。

反例:作为函数参数时,void g(int arr[]) / void g(int arr[10]) 都会调整为 void g(int* arr),这就是退化最常见的地方。


2) 结构体(struct)和类(class)在 C++ 中的主要区别

在 C++ 里,structclass 几乎一样,主要差别只有默认规则:

  1. 默认成员访问权限
  • struct:默认 public
  • class:默认 private
  1. 默认继承方式
  • struct Derived : Base 默认是 public 继承
  • class Derived : Base 默认是 private 继承

除此之外(构造/析构、成员函数、虚函数、模板、重载等)完全相同
惯例上:struct 常用于“数据聚合/简单载体”,class 常用于“封装+不变式”。


3) 什么情况下应该使用共用体(union)?

union 让多个成员共享同一块内存,同一时刻只能“有效地”表示其中一种类型。

适用场景(现代 C++ 更强调谨慎使用):

  1. 节省内存:大型数据结构里多个字段互斥出现(例如协议包、AST 节点的多种形态)。
  2. 与底层二进制/硬件/协议对接:需要按不同视角解释同一段字节(但要注意严格别名/未定义行为问题)。
  3. 实现“变体类型”:一种对象可能是 int 或 double 或 string 等。

现代 C++ 的更推荐方案:

  • 需要“安全的变体类型”优先用 std::variant(带类型标签、析构正确、访问安全)。
  • union 更适合非常底层、极致内存布局控制的场景,且通常要自己维护“当前活跃成员”的判别字段。

4) 为什么 C++11 要引入强类型枚举(enum class)?

传统 enum 的痛点:

  1. 会隐式转换成整数,容易混用/误用:
   enum Color { Red };
   int x = Red; // 允许(容易出错)
  1. 枚举名污染外层作用域(名字冲突)
   enum Color { Red };
   enum Stop  { Red }; // 冲突
  1. 底层类型/大小不够可控(跨平台 ABI、内存布局、序列化不稳定)

enum class(强类型枚举)解决:

  • 不再隐式转 int:类型更安全
  • 作用域限定Color::Red
  • 可指定底层类型enum class Color : uint8_t { ... }
    更利于二进制协议、节省空间、稳定布局

5) autodecltype 的主要区别

两者都用于“推导类型”,但推导规则和使用场景不同。

auto

  • 用于从初始化表达式推导变量类型
  • 推导时更像“模板类型推导”,会丢掉一些顶层属性:
  • 通常会丢顶层 const
  • 引用需要写 auto& / auto&& 才保留

例:

int x = 1;
int& r = x;

auto a = r;   // a 是 int(引用被丢掉)
auto& b = r;  // b 是 int&(保留引用)

decltype

  • 用于获取某个表达式的精确类型(包括引用性)
  • 特别规则:decltype((x))(注意双括号)会得到引用类型

例:

int x = 1;
decltype(x)  a = x;   // int
decltype((x)) b = x;  // int&  (因为 (x) 是左值表达式)

常见搭配

  • decltype(auto):让返回类型/变量类型“完全按表达式精确推导”,常用于完美转发/返回引用:
decltype(auto) f() { return (x); } // 可能返回引用

4. 函数

├── 4.1 函数基础
│   ├── 函数定义与声明
│   ├── 函数调用
│   ├── 函数参数
│   │   ├── 值传递
│   │   ├── 引用传递
│   │   └── 指针传递
│   └── 函数返回
│       ├── 返回类型
│       ├── 返回引用
│       └── 返回指针
├── 4.2 函数特性
│   ├── 内联函数(inline)
│   ├── 默认参数
│   ├── 函数重载
│   ├── 函数模板
│   └── 递归函数
├── 4.3 函数指针
│   ├── 函数指针定义
│   ├── 函数指针作为参数
│   └── 函数指针数组
└── 4.4 Lambda表达式(C++11)
    ├── Lambda语法
    ├── 捕获列表
    └── mutable与异常规范

函数是C++程序的基本构建块,是实现模块化和代码重用的核心。本章将深入讲解函数的各个方面。

4.1 函数基础

4.1.1 函数定义与声明

// 示例4.1:函数的基本使用
#include <iostream>
using namespace std;

// 1. 函数声明(原型) - 告诉编译器函数的存在
// 函数声明只需要:返回类型、函数名、参数列表
int add(int a, int b);  // 函数声明
void printMessage();    // 无参数函数声明
double calculate(double x, double y);  // 声明可以没有参数名

// 2. 函数定义 - 实现函数功能
// 函数定义必须提供函数体
int add(int a, int b) {  // 函数头:返回类型 函数名(参数列表)
    // 函数体
    int sum = a + b;     // 局部变量
    return sum;          // 返回值
}

// 3. 无参数函数
void printMessage() {
    cout << "Hello from function!" << endl;
    // void函数可以不写return,或写 return; 表示返回
}

// 4. 无返回值函数
void printSum(int a, int b) {
    int sum = a + b;
    cout << "Sum = " << sum << endl;
    // 没有return语句,或使用 return;(可选)
}

// 5. 主函数 - 程序入口
int main() {
    // 函数调用
    int result = add(5, 3);  // 实际参数(实参):5和3
    cout << "5 + 3 = " << result << endl;

    printMessage();
    printSum(10, 20);

    // 调用已声明的函数
    double calcResult = calculate(3.14, 2.5);
    cout << "calculate result = " << calcResult << endl;

    return 0;
}

// 6. 函数可以在声明之后定义
double calculate(double x, double y) {
    return x * y + x / y;
}

重要概念

  1. 函数声明:告诉编译器函数的存在,包括返回类型、函数名和参数列表
  2. 函数定义:提供函数的具体实现
  3. 函数调用:使用函数执行特定任务
  4. 实参与形参
  • 形参:函数定义时的参数(如int a, int b
  • 实参:函数调用时传递的参数(如5, 3

4.1.2 参数传递的三种方式

// 示例4.2:参数传递方式
#include <iostream>
using namespace std;

// 1. 值传递(传值) - 默认方式
// 创建实参的副本,函数内修改不影响原始数据
void valuePass(int x) {
    x = x * 2;  // 修改的是副本
    cout << "函数内: x = " << x << endl;
}

// 2. 引用传递(传引用)
// 传递变量的引用(别名),函数内修改会影响原始数据
void referencePass(int &x) {  // &表示引用
    x = x * 2;  // 修改的是原始变量
    cout << "函数内: x = " << x << endl;
}

// 3. 指针传递(传地址)
// 传递变量的地址,通过指针访问和修改原始数据
void pointerPass(int *x) {  // *表示指针
    *x = *x * 2;  // 通过指针修改原始变量
    cout << "函数内: *x = " << *x << endl;
}

// 4. 对比示例
void demonstratePassing() {
    int a = 5;
    cout << "原始值: a = " << a << endl;

    // 值传递
    valuePass(a);
    cout << "值传递后: a = " << a << endl;  // a不变

    // 引用传递
    referencePass(a);
    cout << "引用传递后: a = " << a << endl;  // a改变

    // 指针传递
    pointerPass(&a);  // 传递地址
    cout << "指针传递后: a = " << a << endl;  // a改变

    cout << endl;
}

// 5. 选择参数传递方式的准则
// 小对象(如基本类型):值传递或引用传递
// 大对象(如结构体、类):引用传递,避免拷贝开销
// 需要修改原始数据:引用传递或指针传递
// 不希望修改原始数据:值传递或const引用传递

// 6. 常量引用传递(重要!)
// 避免拷贝开销,同时防止修改原始数据
void printLargeObject(const string &str) {  // const引用
    // str[0] = 'A';  // 错误!不能修改const引用
    cout << str << endl;
}

// 7. 数组作为参数(特殊)
// 数组作为参数时,实际上传递的是数组首元素的指针
void printArray(int arr[], int size) {  // 等价于 int* arr
    for (int i = 0; i < size; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

// 传递多维数组必须指定除第一维外的所有维度
void printMatrix(int matrix[][3], int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 3; j++) {
            cout << matrix[i][j] << " ";
        }
        cout << endl;
    }
}

int main() {
    demonstratePassing();

    // 常量引用示例
    string longString = "This is a very long string...";
    printLargeObject(longString);

    // 数组参数示例
    int arr[] = {1, 2, 3, 4, 5};
    printArray(arr, 5);

    // 多维数组参数示例
    int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
    printMatrix(matrix, 2);

    return 0;
}

易错点提示

  1. 值传递的局限性
   void swap(int a, int b) {  // 错误!无法交换原始值
       int temp = a;
       a = b;
       b = temp;
   }

   // 正确写法:使用引用或指针
   void swap(int &a, int &b) {  // 正确
       int temp = a;
       a = b;
       b = temp;
   }
  1. 返回局部变量的引用或指针
   int& badFunction() {
       int x = 10;  // 局部变量
       return x;    // 危险!返回局部变量的引用
   }  // x被销毁,返回的引用无效(悬空引用)

4.1.3 返回类型与返回值

// 示例4.3:返回类型与返回值
#include <iostream>
#include <vector>
using namespace std;

// 1. 基本返回类型
int getSum(int a, int b) {
    return a + b;  // 返回int类型
}

// 2. 返回引用
// 可以返回全局变量、静态变量、动态分配的内存或传入的引用参数的引用
int& getElement(int arr[], int index) {
    return arr[index];  // 返回数组元素的引用
}

// 3. 返回指针
int* createArray(int size) {
    int* arr = new int[size];  // 动态分配
    for (int i = 0; i < size; i++) {
        arr[i] = i * 2;
    }
    return arr;  // 返回动态分配的内存的指针
}

// 4. 返回结构体/类对象
struct Point {
    int x, y;
};

Point createPoint(int x, int y) {
    Point p;
    p.x = x;
    p.y = y;
    return p;  // 返回结构体对象(可能涉及拷贝)
}

// 5. 返回自动类型推导(C++14)
auto multiply(int a, double b) {
    return a * b;  // 返回类型自动推导为double
}

// 6. 无返回值(void)
void processData(int value) {
    if (value < 0) {
        return;  // 提前返回
    }
    cout << "Processing: " << value << endl;
    // 函数结束时自动返回,不需要return语句
}

// 7. 返回多个值的方法
// 方法1:通过引用参数返回
void getMinMax(int arr[], int size, int &min, int &max) {
    min = arr[0];
    max = arr[0];
    for (int i = 1; i < size; i++) {
        if (arr[i] < min) min = arr[i];
        if (arr[i] > max) max = arr[i];
    }
}

// 方法2:返回结构体
struct MinMax {
    int min;
    int max;
};

MinMax findMinMax(int arr[], int size) {
    MinMax result;
    result.min = arr[0];
    result.max = arr[0];
    for (int i = 1; i < size; i++) {
        if (arr[i] < result.min) result.min = arr[i];
        if (arr[i] > result.max) result.max = arr[i];
    }
    return result;
}

// 8. C++17结构化绑定(返回多个值的便捷方式)
#include <tuple>
tuple<int, int, double> calculateStats(int a, int b) {
    int sum = a + b;
    int product = a * b;
    double average = (a + b) / 2.0;
    return make_tuple(sum, product, average);  // 返回tuple
}

int main() {
    // 基本返回类型
    int sum = getSum(5, 3);
    cout << "Sum: " << sum << endl;

    // 返回引用
    int numbers[] = {10, 20, 30, 40, 50};
    getElement(numbers, 2) = 100;  // 修改数组元素
    cout << "numbers[2] = " << numbers[2] << endl;

    // 返回指针
    int* arr = createArray(5);
    cout << "Dynamic array: ";
    for (int i = 0; i < 5; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    delete[] arr;  // 必须手动释放内存!

    // 返回结构体
    Point p = createPoint(3, 4);
    cout << "Point: (" << p.x << ", " << p.y << ")" << endl;

    // 自动类型推导
    auto result = multiply(3, 2.5);
    cout << "Multiply result: " << result 
         << ", type: " << typeid(result).name() << endl;

    // 通过引用参数返回多个值
    int min, max;
    getMinMax(numbers, 5, min, max);
    cout << "Min: " << min << ", Max: " << max << endl;

    // 返回结构体获取多个值
    MinMax mm = findMinMax(numbers, 5);
    cout << "MinMax struct - Min: " << mm.min << ", Max: " << mm.max << endl;

    // 使用tuple返回多个值
    auto stats = calculateStats(10, 20);
    cout << "Tuple - Sum: " << get<0>(stats) 
         << ", Product: " << get<1>(stats)
         << ", Average: " << get<2>(stats) << endl;

    // C++17结构化绑定(更方便)
    auto [s, pdt, avg] = calculateStats(30, 40);
    cout << "Structured binding - Sum: " << s 
         << ", Product: " << pdt
         << ", Average: " << avg << endl;

    return 0;
}

重要概念

  1. 返回引用:必须确保返回的引用在函数调用后仍然有效
  2. 返回值优化(RVO):编译器可能会优化返回对象的拷贝
  3. 返回类型推导:C++14允许使用auto推导返回类型
  4. 尾返回类型:C++11引入,用于复杂返回类型推导

4.2 函数特性

4.2.1 内联函数(inline)

// 示例4.4:内联函数
#include <iostream>
using namespace std;

// 1. 内联函数定义
// 内联函数建议编译器在调用处展开代码,避免函数调用开销
// 适用于短小、频繁调用的函数
inline int square(int x) {
    return x * x;
}

// 2. 内联函数与宏的区别
// 宏是预处理器的文本替换,没有类型检查
#define SQUARE_MACRO(x) ((x) * (x))

// 内联函数有类型检查,更安全
inline double square(double x) {
    return x * x;
}

// 3. 类内定义的成员函数自动为内联函数
class Calculator {
public:
    // 类内定义的函数默认为inline
    int add(int a, int b) { return a + b; }

    // 类内声明,类外定义的函数需要显式指定inline
    int multiply(int a, int b);
};

// 类外定义内联函数
inline int Calculator::multiply(int a, int b) {
    return a * b;
}

// 4. 内联函数的限制
// - 不能包含循环、switch等复杂控制结构
// - 不能是递归函数
// - 函数体不能过大
// 注意:inline只是建议,编译器可能忽略

// 5. 正确使用内联函数的示例
inline int max(int a, int b) {
    return (a > b) ? a : b;
}

inline bool isEven(int n) {
    return (n % 2 == 0);
}

int main() {
    // 内联函数调用
    int a = 5;
    cout << "square(5) = " << square(5) << endl;
    cout << "square(3.14) = " << square(3.14) << endl;

    // 宏调用
    cout << "SQUARE_MACRO(5) = " << SQUARE_MACRO(5) << endl;

    // 宏的陷阱
    int x = 5;
    cout << "SQUARE_MACRO(++x) = " << SQUARE_MACRO(++x) << endl;  // 危险!x被增加两次

    x = 5;
    cout << "square(++x) = " << square(++x) << endl;  // 安全,x只增加一次

    // 类成员函数
    Calculator calc;
    cout << "calc.add(3, 4) = " << calc.add(3, 4) << endl;
    cout << "calc.multiply(3, 4) = " << calc.multiply(3, 4) << endl;

    // 使用内联函数
    cout << "max(10, 20) = " << max(10, 20) << endl;
    cout << "isEven(7) = " << isEven(7) << endl;

    return 0;
}

4.2.2 默认参数

// 示例4.5:默认参数
#include <iostream>
#include <string>
using namespace std;

// 1. 基本默认参数
// 默认参数必须在函数声明中指定,通常在头文件中
void display(string message, int times = 1) {
    for (int i = 0; i < times; i++) {
        cout << message << endl;
    }
}

// 2. 多个默认参数
// 默认参数必须从右向左连续设置
void drawRectangle(int width = 10, int height = 5, char symbol = '*') {
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            cout << symbol;
        }
        cout << endl;
    }
}

// 3. 默认参数与函数重载
// 默认参数可以减少函数重载的需要
void print(int value, string label = "Value") {
    cout << label << ": " << value << endl;
}

// 4. 默认参数在指针/引用函数中的使用
void initArray(int* arr, int size, int initValue = 0) {
    for (int i = 0; i < size; i++) {
        arr[i] = initValue;
    }
}

// 5. 默认参数与内联函数结合
inline void logMessage(const string& msg, bool withTimestamp = false) {
    if (withTimestamp) {
        // 这里简化处理,实际应用中会获取当前时间
        cout << "[TIME] " << msg << endl;
    } else {
        cout << msg << endl;
    }
}

// 6. 默认参数的声明与定义
// 通常默认参数在函数声明中指定
void setColor(int r, int g = 0, int b = 0);

int main() {
    // 使用默认参数
    display("Hello");           // 使用默认times=1
    display("World", 3);        // 指定times=3

    cout << endl;

    // 多个默认参数的使用
    cout << "默认矩形:" << endl;
    drawRectangle();            // 全部使用默认值

    cout << "\n指定宽度:" << endl;
    drawRectangle(20);          // 只指定width

    cout << "\n指定宽度和高度:" << endl;
    drawRectangle(15, 8);       // 指定width和height

    cout << "\n指定所有参数:" << endl;
    drawRectangle(12, 6, '#');  // 指定所有参数

    // 默认参数与函数调用
    print(42);                  // 使用默认label
    print(100, "Number");       // 指定label

    // 初始化数组
    int arr[5];
    initArray(arr, 5);          // 使用默认initValue=0
    cout << "初始化后的数组:";
    for (int i = 0; i < 5; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;

    initArray(arr, 5, -1);      // 指定initValue=-1
    cout << "重新初始化后的数组:";
    for (int i = 0; i < 5; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;

    // 内联函数与默认参数
    logMessage("Info message");
    logMessage("Important message", true);

    return 0;
}

// 函数定义(声明中已有默认参数,定义中不应重复)
void setColor(int r, int g, int b) {
    cout << "Color: RGB(" << r << ", " << g << ", " << b << ")" << endl;
}

易错点提示

  1. 默认参数的位置
   // 错误!默认参数必须从右向左
   // void func(int a = 1, int b, int c = 2);

   // 正确
   void func(int a, int b = 2, int c = 3);
  1. 默认参数与函数重载冲突
   void display(string msg, int times = 1);
   void display(string msg);  // 重载冲突!调用display("hello")有歧义
  1. 默认参数在声明和定义中的一致性
   // 头文件中声明
   void func(int a, int b = 10);

   // 源文件中定义(不能重复默认参数)
   // void func(int a, int b = 10) { ... }  // 错误!重复默认参数
   void func(int a, int b) { ... }  // 正确

4.2.3 函数重载

// 示例4.6:函数重载
#include <iostream>
#include <string>
#include <cmath>
using namespace std;

// 1. 函数重载:同一作用域内,函数名相同但参数列表不同
// 参数列表不同包括:参数类型不同、参数个数不同、参数顺序不同

// 2. 参数类型不同的重载
int add(int a, int b) {
    cout << "调用 add(int, int)" << endl;
    return a + b;
}

double add(double a, double b) {
    cout << "调用 add(double, double)" << endl;
    return a + b;
}

string add(const string& a, const string& b) {
    cout << "调用 add(string, string)" << endl;
    return a + b;
}

// 3. 参数个数不同的重载
int sum(int a) {
    return a;
}

int sum(int a, int b) {
    return a + b;
}

int sum(int a, int b, int c) {
    return a + b + c;
}

// 4. 参数顺序不同的重载
void display(int id, string name) {
    cout << "ID: " << id << ", Name: " << name << endl;
}

void display(string name, int id) {
    cout << "Name: " << name << ", ID: " << id << endl;
}

// 5. 函数重载与默认参数的关系
// 注意:可能导致二义性
void print(int x) {
    cout << "print(int): " << x << endl;
}

void print(int x, int y = 0) {
    cout << "print(int, int): " << x << ", " << y << endl;
}
// print(5);  // 错误!有歧义,可以调用print(int)或print(int, int=0)

// 6. 函数重载与const参数
// const可以用于区分重载函数
void process(int& x) {
    cout << "process(int&): 可修改" << endl;
    x++;
}

void process(const int& x) {
    cout << "process(const int&): 不可修改" << endl;
    // x++;  // 错误!x是const
}

// 7. 函数重载与指针/引用参数
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

// 8. 重载解析过程(编译器如何选择重载函数)
// 1) 精确匹配
// 2) 类型提升(如char→int,float→double)
// 3) 标准转换(如int→double)
// 4) 用户定义转换
// 5) 可变参数匹配

void test(int x) {
    cout << "test(int)" << endl;
}

void test(double x) {
    cout << "test(double)" << endl;
}

void test(int x, double y) {
    cout << "test(int, double)" << endl;
}

int main() {
    // 参数类型不同的重载
    cout << "add(3, 4) = " << add(3, 4) << endl;
    cout << "add(3.14, 2.5) = " << add(3.14, 2.5) << endl;
    cout << "add(\"Hello, \", \"World!\") = " << add("Hello, ", "World!") << endl;

    // 参数个数不同的重载
    cout << "\nsum(5) = " << sum(5) << endl;
    cout << "sum(5, 6) = " << sum(5, 6) << endl;
    cout << "sum(5, 6, 7) = " << sum(5, 6, 7) << endl;

    // 参数顺序不同的重载
    cout << endl;
    display(101, "Alice");
    display("Bob", 102);

    // const参数重载
    cout << endl;
    int num = 10;
    const int const_num = 20;

    process(num);        // 调用 process(int&)
    process(const_num);  // 调用 process(const int&)
    process(30);         // 调用 process(const int&),字面量是右值

    // 指针/引用参数重载
    cout << endl;
    int x = 5, y = 10;
    cout << "交换前: x = " << x << ", y = " << y << endl;
    swap(&x, &y);  // 调用 swap(int*, int*)
    cout << "指针交换后: x = " << x << ", y = " << y << endl;

    swap(x, y);    // 调用 swap(int&, int&)
    cout << "引用交换后: x = " << x << ", y = " << y << endl;

    // 重载解析
    cout << "\n重载解析测试:" << endl;
    char c = 'A';
    float f = 3.14f;

    test(c);     // char → int(类型提升),调用 test(int)
    test(f);     // float → double(标准转换),调用 test(double)
    test(5, f);  // 精确匹配 test(int, double)

    // 9. 不能仅靠返回类型区分重载函数
    // int getValue();      // 错误!与下面冲突
    // double getValue();   // 仅返回类型不同,不能重载

    return 0;
}

4.2.4 递归函数

// 示例4.7:递归函数
#include <iostream>
using namespace std;

// 1. 递归函数:函数直接或间接调用自身
// 必须有递归终止条件,否则会无限递归(栈溢出)

// 2. 阶乘计算(经典递归示例)
// n! = n × (n-1)!,其中0! = 1
long long factorial(int n) {
    // 终止条件
    if (n <= 1) {
        return 1;
    }
    // 递归调用
    return n * factorial(n - 1);
}

// 3. 斐波那契数列
// F(0) = 0, F(1) = 1, F(n) = F(n-1) + F(n-2)
int fibonacci(int n) {
    if (n <= 0) return 0;
    if (n == 1) return 1;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

// 4. 尾递归优化版本(效率更高)
// 尾递归:递归调用是函数体最后执行的操作
long long factorialTail(int n, long long result = 1) {
    if (n <= 1) {
        return result;
    }
    return factorialTail(n - 1, n * result);  // 尾递归调用
}

// 5. 递归的优缺点
// 优点:代码简洁,适合解决分治、回溯等问题
// 缺点:效率较低(函数调用开销),可能栈溢出

// 6. 汉诺塔问题
void hanoi(int n, char from, char to, char aux) {
    if (n == 1) {
        cout << "移动盘子 1 从 " << from << " 到 " << to << endl;
        return;
    }

    hanoi(n - 1, from, aux, to);
    cout << "移动盘子 " << n << " 从 " << from << " 到 " << to << endl;
    hanoi(n - 1, aux, to, from);
}

// 7. 递归实现二分查找
int binarySearch(int arr[], int left, int right, int target) {
    if (left > right) {
        return -1;  // 未找到
    }

    int mid = left + (right - left) / 2;

    if (arr[mid] == target) {
        return mid;  // 找到
    } else if (arr[mid] > target) {
        return binarySearch(arr, left, mid - 1, target);  // 在左半部分查找
    } else {
        return binarySearch(arr, mid + 1, right, target);  // 在右半部分查找
    }
}

// 8. 递归实现数组求和
int arraySum(int arr[], int n) {
    if (n <= 0) {
        return 0;
    }
    return arraySum(arr, n - 1) + arr[n - 1];
}

// 9. 递归实现字符串反转
void reverseString(char str[], int start, int end) {
    if (start >= end) {
        return;
    }

    // 交换首尾字符
    char temp = str[start];
    str[start] = str[end];
    str[end] = temp;

    // 递归处理剩余部分
    reverseString(str, start + 1, end - 1);
}

// 10. 递归深度和栈溢出
void deepRecursion(int depth) {
    if (depth <= 0) {
        return;
    }

    // 每次递归调用都会在栈上分配内存
    // 如果递归太深,会导致栈溢出
    cout << "深度: " << depth << endl;
    deepRecursion(depth - 1);
}

int main() {
    // 阶乘计算
    cout << "5! = " << factorial(5) << endl;
    cout << "10! = " << factorial(10) << endl;
    cout << "20! = " << factorialTail(20) << " (尾递归)" << endl;

    // 斐波那契数列
    cout << "\n斐波那契数列前10项: ";
    for (int i = 0; i < 10; i++) {
        cout << fibonacci(i) << " ";
    }
    cout << endl;

    // 汉诺塔问题
    cout << "\n汉诺塔问题(3个盘子):" << endl;
    hanoi(3, 'A', 'C', 'B');

    // 二分查找
    int sortedArr[] = {1, 3, 5, 7, 9, 11, 13, 15};
    int size = sizeof(sortedArr) / sizeof(sortedArr[0]);
    int target = 7;
    int index = binarySearch(sortedArr, 0, size - 1, target);
    cout << "\n二分查找 " << target << ": ";
    if (index != -1) {
        cout << "找到,索引为 " << index << endl;
    } else {
        cout << "未找到" << endl;
    }

    // 数组求和
    int arr[] = {1, 2, 3, 4, 5};
    int sum = arraySum(arr, 5);
    cout << "\n数组求和: " << sum << endl;

    // 字符串反转
    char str[] = "Hello, World!";
    cout << "原始字符串: " << str << endl;
    reverseString(str, 0, strlen(str) - 1);
    cout << "反转后字符串: " << str << endl;

    // 注意:递归深度过大可能导致栈溢出
    // 尝试调用 deepRecursion(10000);  // 可能导致栈溢出

    return 0;
}

递归重要概念

  1. 递归三要素
  • 递归定义:问题的定义是递归的
  • 递归终止条件:必须有明确的结束条件
  • 递归调用:每次调用都向终止条件靠近
  1. 递归 vs 迭代
  • 递归:代码简洁,但效率低,可能栈溢出
  • 迭代:代码可能复杂,但效率高,不会栈溢出
  1. 尾递归优化
  • 某些编译器可以将尾递归优化为迭代,避免栈溢出
  • 尾递归要求递归调用是函数体中最后执行的操作

4.3 函数指针

// 示例4.8:函数指针
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;

// 1. 函数指针基础
// 函数指针:指向函数的指针,可以像函数一样调用

// 函数声明
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
int divide(int a, int b);

// 函数定义
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int divide(int a, int b) { 
    if (b != 0) return a / b;
    return 0;
}

// 2. 函数指针类型定义
// 方式1:直接定义函数指针
int (*funcPtr1)(int, int);  // 指向返回int,接受两个int参数的函数

// 方式2:使用typedef定义函数指针类型
typedef int (*MathFunc)(int, int);  // MathFunc是函数指针类型

// 方式3:使用using(C++11)
using MathFunc2 = int (*)(int, int);

// 3. 函数指针的使用
void demonstrateFunctionPointers() {
    cout << "函数指针演示:" << endl;

    // 将函数地址赋给函数指针
    MathFunc func = add;
    cout << "5 + 3 = " << func(5, 3) << endl;

    func = subtract;
    cout << "5 - 3 = " << func(5, 3) << endl;

    // 直接使用函数指针
    int (*ptr)(int, int) = multiply;
    cout << "5 * 3 = " << ptr(5, 3) << endl;

    // 函数指针数组
    MathFunc operations[] = {add, subtract, multiply, divide};
    char opSymbols[] = {'+', '-', '*', '/'};

    int a = 10, b = 5;
    for (int i = 0; i < 4; i++) {
        cout << a << " " << opSymbols[i] << " " << b 
             << " = " << operations[i](a, b) << endl;
    }
}

// 4. 函数指针作为参数(回调函数)
void processNumbers(int a, int b, MathFunc operation) {
    int result = operation(a, b);
    cout << "处理结果: " << result << endl;
}

// 5. 函数指针作为返回值
MathFunc getOperation(char op) {
    switch (op) {
        case '+': return add;
        case '-': return subtract;
        case '*': return multiply;
        case '/': return divide;
        default: return nullptr;
    }
}

// 6. 标准库中的函数指针应用
bool compareDesc(int a, int b) {
    return a > b;  // 降序比较
}

bool isEven(int n) {
    return n % 2 == 0;
}

void demonstrateSTLWithFunctionPointers() {
    cout << "\nSTL中使用函数指针:" << endl;

    vector<int> numbers = {5, 2, 8, 1, 9, 3};

    // 使用函数指针作为比较函数
    sort(numbers.begin(), numbers.end(), compareDesc);

    cout << "降序排序: ";
    for (int num : numbers) {
        cout << num << " ";
    }
    cout << endl;

    // 使用函数指针作为谓词
    auto it = find_if(numbers.begin(), numbers.end(), isEven);
    if (it != numbers.end()) {
        cout << "找到第一个偶数: " << *it << endl;
    }
}

// 7. 函数指针与函数对象的对比
// 函数对象(仿函数)通常比函数指针更灵活,可以被内联

// 8. C++11:使用std::function(更灵活)
#include <functional>
void demonstrateStdFunction() {
    cout << "\n使用std::function:" << endl;

    // std::function可以存储任何可调用对象
    std::function<int(int, int)> func;

    func = add;
    cout << "add(7, 3) = " << func(7, 3) << endl;

    // 可以存储lambda表达式
    func = [](int a, int b) { return a * b; };
    cout << "lambda multiply(7, 3) = " << func(7, 3) << endl;

    // 可以存储函数对象
    struct Power {
        int operator()(int a, int b) { 
            return static_cast<int>(pow(a, b));
        }
    };

    Power power;
    func = power;
    cout << "power(2, 3) = " << func(2, 3) << endl;
}

// 9. 成员函数指针(指向类成员函数的指针)
class Calculator {
public:
    int add(int a, int b) { return a + b; }
    int subtract(int a, int b) { return a - b; }
    static int multiply(int a, int b) { return a * b; }
};

void demonstrateMemberFunctionPointers() {
    cout << "\n成员函数指针:" << endl;

    Calculator calc;

    // 普通成员函数指针
    int (Calculator::*memberFunc)(int, int) = &Calculator::add;
    cout << "calc.add(5, 3) = " << (calc.*memberFunc)(5, 3) << endl;

    memberFunc = &Calculator::subtract;
    cout << "calc.subtract(5, 3) = " << (calc.*memberFunc)(5, 3) << endl;

    // 静态成员函数指针(与普通函数指针类似)
    int (*staticFunc)(int, int) = &Calculator::multiply;
    cout << "Calculator::multiply(5, 3) = " << staticFunc(5, 3) << endl;
}

int main() {
    demonstrateFunctionPointers();

    // 回调函数示例
    cout << "\n回调函数示例:" << endl;
    processNumbers(20, 4, add);
    processNumbers(20, 4, divide);

    // 返回函数指针
    MathFunc op = getOperation('*');
    if (op != nullptr) {
        cout << "\n返回的函数指针: 8 * 2 = " << op(8, 2) << endl;
    }

    demonstrateSTLWithFunctionPointers();
    demonstrateStdFunction();
    demonstrateMemberFunctionPointers();

    return 0;
}

函数指针重要概念

  1. 函数指针的声明语法
   // 返回类型 (*指针名)(参数列表)
   int (*funcPtr)(int, int);
  1. 函数指针 vs 函数对象
  • 函数指针:指向函数的指针,语法较复杂
  • 函数对象:重载了()运算符的类对象,更灵活
  • std::function:C++11引入,可以存储任何可调用对象
  1. 成员函数指针的特殊语法
   // 类名::*指针名
   int (ClassName::*memberPtr)(int);

   // 调用语法
   (object.*memberPtr)(args);
   (ptr->*memberPtr)(args);

4.4 Lambda表达式(C++11)

// 示例4.9:Lambda表达式
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

int main() {
    // 1. Lambda表达式基本语法
    // [捕获列表](参数列表) -> 返回类型 { 函数体 }

    // 最简单的lambda:没有参数,没有返回值
    auto simpleLambda = []() {
        cout << "Hello from lambda!" << endl;
    };
    simpleLambda();

    // 2. 带参数的lambda
    auto add = [](int a, int b) -> int {
        return a + b;
    };
    cout << "add(3, 4) = " << add(3, 4) << endl;

    // 返回类型可以自动推导(C++14)
    auto multiply = [](auto a, auto b) {  // C++14泛型lambda
        return a * b;
    };
    cout << "multiply(3, 4.5) = " << multiply(3, 4.5) << endl;

    // 3. 捕获列表
    // []:不捕获任何外部变量
    // [=]:以值方式捕获所有外部变量
    // [&]:以引用方式捕获所有外部变量
    // [x]:以值方式捕获x
    // [&x]:以引用方式捕获x
    // [=, &x]:以值方式捕获所有变量,但x以引用方式捕获
    // [&, x]:以引用方式捕获所有变量,但x以值方式捕获

    int x = 10, y = 20;

    // 值捕获
    auto valueCapture = [x, y]() {
        cout << "值捕获: x = " << x << ", y = " << y << endl;
        // x++;  // 错误!值捕获的变量在lambda内是const(除非使用mutable)
    };
    valueCapture();

    // 引用捕获
    auto referenceCapture = [&x, &y]() {
        x++;
        y++;
        cout << "引用捕获: x = " << x << ", y = " << y << endl;
    };
    referenceCapture();
    cout << "修改后: x = " << x << ", y = " << y << endl;

    // 混合捕获
    auto mixedCapture = [x, &y]() {
        // x是值捕获(副本),y是引用捕获
        cout << "混合捕获: x = " << x << ", y = " << y << endl;
    };
    mixedCapture();

    // 4. mutable关键字
    // 允许修改值捕获的变量(修改的是副本)
    int count = 0;
    auto counter = [count]() mutable {
        cout << "计数: " << count << endl;
        count++;  // 修改副本,不影响外部的count
    };

    for (int i = 0; i < 3; i++) {
        counter();
    }
    cout << "外部count: " << count << endl;  // 仍然是0

    // 5. Lambda在STL算法中的应用
    vector<int> numbers = {5, 2, 8, 1, 9, 3, 7, 4, 6};

    cout << "\n原始数组: ";
    for_each(numbers.begin(), numbers.end(), [](int n) {
        cout << n << " ";
    });
    cout << endl;

    // 排序(使用lambda作为比较函数)
    sort(numbers.begin(), numbers.end(), [](int a, int b) {
        return a > b;  // 降序排序
    });

    cout << "降序排序后: ";
    for_each(numbers.begin(), numbers.end(), [](int n) {
        cout << n << " ";
    });
    cout << endl;

    // 查找第一个大于5的元素
    auto it = find_if(numbers.begin(), numbers.end(), [](int n) {
        return n > 5;
    });

    if (it != numbers.end()) {
        cout << "第一个大于5的元素: " << *it << endl;
    }

    // 统计偶数个数
    int evenCount = count_if(numbers.begin(), numbers.end(), [](int n) {
        return n % 2 == 0;
    });
    cout << "偶数个数: " << evenCount << endl;

    // 6. 返回lambda的函数
    auto makeMultiplier = [](int factor) {
        // 返回一个lambda
        return [factor](int value) {
            return value * factor;
        };
    };

    auto doubleIt = makeMultiplier(2);
    auto tripleIt = makeMultiplier(3);

    cout << "doubleIt(5) = " << doubleIt(5) << endl;
    cout << "tripleIt(5) = " << tripleIt(5) << endl;

    // 7. Lambda作为回调函数
    auto processData = [](const vector<int>& data, function<void(int)> callback) {
        for (int value : data) {
            callback(value);
        }
    };

    cout << "\n回调处理: ";
    processData(numbers, [](int value) {
        cout << value << " ";
    });
    cout << endl;

    // 8. 泛型Lambda(C++14)
    auto genericAdd = [](auto a, auto b) {
        return a + b;
    };

    cout << "泛型lambda: " << endl;
    cout << "genericAdd(3, 4) = " << genericAdd(3, 4) << endl;
    cout << "genericAdd(3.14, 2.5) = " << genericAdd(3.14, 2.5) << endl;
    cout << "genericAdd(string(\"Hello, \"), string(\"World!\")) = " 
         << genericAdd(string("Hello, "), string("World!")) << endl;

    // 9. 初始化捕获(C++14)
    int base = 100;
    auto addBase = [value = base + 10](int x) {  // 初始化捕获
        return x + value;
    };
    cout << "初始化捕获: addBase(5) = " << addBase(5) << endl;

    // 10. 将lambda存储在std::function中
    function<int(int, int)> func;

    func = [](int a, int b) { return a + b; };
    cout << "function存储lambda: " << func(10, 20) << endl;

    // 11. Lambda表达式类型
    // 每个lambda都有唯一的类型,可以使用auto或std::function存储
    auto uniqueLambda = []() { return 42; };
    // decltype(uniqueLambda) anotherLambda;  // 错误!不能默认构造

    return 0;
}

Lambda表达式重要概念

  1. Lambda的组成
  • 捕获列表:指定如何捕获外部变量
  • 参数列表:与普通函数参数类似
  • 返回类型:可以自动推导或显式指定
  • 函数体:实现功能的代码块
  1. 捕获方式
  • 值捕获:创建外部变量的副本
  • 引用捕获:使用外部变量的引用
  • 初始化捕获(C++14):可以初始化捕获的变量
  1. mutable关键字
  • 允许修改值捕获的变量(修改的是副本)
  • 不影响外部变量
  1. Lambda的应用场景
  • STL算法的谓词和比较函数
  • 回调函数
  • 创建闭包(携带状态的函数对象)
  • 简化一次性使用的函数

综合例题与解析

// 例题1:计算器程序(综合应用函数特性)
#include <iostream>
#include <functional>
#include <map>
#include <cmath>
using namespace std;

// 使用函数指针类型
using Operation = double (*)(double, double);

// 基本运算函数
double add(double a, double b) { return a + b; }
double subtract(double a, double b) { return a - b; }
double multiply(double a, double b) { return a * b; }
double divide(double a, double b) { 
    if (b == 0) throw runtime_error("除数不能为0");
    return a / b; 
}

// 使用函数对象(仿函数)
class Power {
public:
    double operator()(double base, double exponent) const {
        return pow(base, exponent);
    }
};

// 使用lambda
auto modulo = [](double a, double b) -> double {
    if (b == 0) throw runtime_error("除数不能为0");
    return fmod(a, b);
};

// 计算器类
class Calculator {
private:
    map<char, function<double(double, double)>> operations;

public:
    Calculator() {
        // 注册基本运算
        operations['+'] = add;
        operations['-'] = subtract;
        operations['*'] = multiply;
        operations['/'] = divide;

        // 注册函数对象
        operations['^'] = Power();

        // 注册lambda
        operations['%'] = modulo;

        // 注册更多lambda
        operations['@'] = [](double a, double b) { return sqrt(a*a + b*b); };  // 模长
    }

    double calculate(char op, double a, double b) {
        auto it = operations.find(op);
        if (it == operations.end()) {
            throw runtime_error("未知的操作符");
        }

        try {
            return it->second(a, b);
        } catch (const exception& e) {
            throw runtime_error(string("计算错误: ") + e.what());
        }
    }

    void displayOperations() const {
        cout << "支持的操作:" << endl;
        cout << "  + : 加法" << endl;
        cout << "  - : 减法" << endl;
        cout << "  * : 乘法" << endl;
        cout << "  / : 除法" << endl;
        cout << "  ^ : 幂运算" << endl;
        cout << "  % : 取模" << endl;
        cout << "  @ : 计算模长 sqrt(a^2 + b^2)" << endl;
    }
};

// 递归函数:计算表达式树
struct Node {
    char op;      // 操作符,'#'表示数字
    double value; // 如果是数字,存储值
    Node* left;
    Node* right;

    Node(char o, double v = 0) : op(o), value(v), left(nullptr), right(nullptr) {}
};

double evaluateTree(Node* root, Calculator& calc) {
    if (root == nullptr) {
        return 0;
    }

    if (root->op == '#') {
        return root->value;  // 叶子节点,返回数字
    }

    // 递归计算左右子树
    double leftVal = evaluateTree(root->left, calc);
    double rightVal = evaluateTree(root->right, calc);

    // 计算当前节点
    return calc.calculate(root->op, leftVal, rightVal);
}

int main() {
    Calculator calc;
    calc.displayOperations();

    cout << "\n简单计算测试:" << endl;

    try {
        // 测试各种运算
        cout << "10 + 5 = " << calc.calculate('+', 10, 5) << endl;
        cout << "10 - 5 = " << calc.calculate('-', 10, 5) << endl;
        cout << "10 * 5 = " << calc.calculate('*', 10, 5) << endl;
        cout << "10 / 5 = " << calc.calculate('/', 10, 5) << endl;
        cout << "2 ^ 3 = " << calc.calculate('^', 2, 3) << endl;
        cout << "10 % 3 = " << calc.calculate('%', 10, 3) << endl;
        cout << "3 @ 4 = " << calc.calculate('@', 3, 4) << endl;

        // 测试错误情况
        // cout << "10 / 0 = " << calc.calculate('/', 10, 0) << endl;  // 抛出异常
    } catch (const exception& e) {
        cerr << "错误: " << e.what() << endl;
    }

    cout << "\n表达式树计算测试:" << endl;
    // 构建表达式树: (3 + 4) * (10 - 2)
    //        *
    //      /   \
    //     +     -
    //    / \   / \
    //   3   4 10  2

    Node* root = new Node('*');
    root->left = new Node('+');
    root->right = new Node('-');

    root->left->left = new Node('#', 3);
    root->left->right = new Node('#', 4);

    root->right->left = new Node('#', 10);
    root->right->right = new Node('#', 2);

    double result = evaluateTree(root, calc);
    cout << "(3 + 4) * (10 - 2) = " << result << endl;

    // 清理内存
    delete root->left->left;
    delete root->left->right;
    delete root->left;
    delete root->right->left;
    delete root->right->right;
    delete root->right;
    delete root;

    return 0;
}

本章小结

重点回顾

  1. 函数基础:声明、定义、调用,参数传递的三种方式
  2. 返回值:返回类型、返回引用和指针的注意事项
  3. 函数特性:内联函数、默认参数、函数重载、递归函数
  4. 函数指针:指向函数的指针,回调函数的应用
  5. Lambda表达式:匿名函数,捕获列表,在STL中的应用

易错易混淆点

  1. 参数传递选择
  • 小对象:值传递或const引用传递
  • 大对象:const引用传递
  • 需要修改:引用传递或指针传递
  1. 函数重载二义性
  • 默认参数可能导致重载冲突
  • 类型转换可能导致二义性
  1. 递归终止条件
  • 必须有明确的终止条件
  • 递归深度过大会导致栈溢出
  1. 函数指针语法
  • 注意函数指针的声明语法
  • 成员函数指针的特殊调用语法
  1. Lambda捕获
  • 值捕获的是副本,引用捕获的是引用
  • 注意捕获的变量生命周期

考研笔试常见题型

  1. 选择题:考察函数的基本概念和特性
  2. 程序阅读题:分析函数调用、参数传递、返回值
  3. 程序填空题:补全函数定义、调用代码
  4. 编程题:实现特定功能的函数或算法

编程实践建议

  1. 函数设计原则
  • 单一职责:一个函数只做一件事
  • 简洁明了:函数体不宜过长
  • 好的命名:函数名应反映其功能
  1. 参数设计
  • 合理使用默认参数
  • 避免使用输出参数,优先使用返回值
  • 使用const保护不需要修改的参数
  1. 错误处理
  • 使用返回值或异常处理错误
  • 检查参数的有效性
  1. 现代C++特性
  • 优先使用Lambda代替函数指针
  • 使用auto简化返回类型
  • 使用constexpr函数进行编译期计算

学习检查

  1. 你能解释值传递、引用传递和指针传递的区别吗?
  2. 什么情况下应该使用内联函数?
  3. 函数重载和默认参数可能导致什么问题?
  4. 递归函数必须满足什么条件?
  5. Lambda表达式有哪几种捕获方式?各有什么特点?

5. 指针与引用(重点)

├── 5.1 指针基础
│   ├── 指针概念与定义
│   ├── 指针运算符(*和&)
│   ├── 指针运算
│   │   ├── 指针算术运算
│   │   ├── 指针关系运算
│   │   └── 指针与数组
│   └── 指针常量与常量指针
├── 5.2 高级指针
│   ├── 指向指针的指针
│   ├── 指针数组
│   ├── 动态内存分配
│   │   ├── new/delete
│   │   ├── new[]/delete[]
│   │   └── malloc/free(对比)
│   └── 智能指针(C++11)
│       ├── unique_ptr
│       ├── shared_ptr
│       └── weak_ptr
├── 5.3 引用
│   ├── 引用概念与定义
│   ├── 引用作为函数参数
│   ├── 引用作为返回值
│   └── 引用与指针对比
└── 5.4 指针与引用常见考点
    ├── 悬空指针与野指针
    ├── 内存泄漏
    ├── const与指针/引用
    └── 函数参数传递方式选择

指针和引用是C++中最核心、最重要、也最难掌握的概念。它们是理解C++内存管理、高效编程和面向对象设计的基础。

5.1 指针基础

5.1.1 指针的概念与定义

// 示例5.1:指针基础
#include <iostream>
using namespace std;

int main() {
    int var = 10;      // 定义一个整型变量
    int *ptr;          // 定义一个整型指针

    ptr = &var;        // 将var的地址赋给指针ptr

    cout << "变量var的值: " << var << endl;
    cout << "变量var的地址: " << &var << endl;
    cout << "指针ptr存储的地址: " << ptr << endl;
    cout << "通过指针访问var的值: " << *ptr << endl;

    // 修改指针所指向的值
    *ptr = 20;
    cout << "修改后,变量var的值: " << var << endl;

    // 指针的大小(与指向的类型无关,只与系统架构有关)
    cout << "指针ptr的大小: " << sizeof(ptr) << " bytes" << endl;

    // 不同类型的指针
    double d = 3.14;
    double *dptr = &d;
    cout << "double指针的大小: " << sizeof(dptr) << " bytes" << endl;

    // 空指针
    int *nullPtr = nullptr;  // C++11引入的空指针常量
    // 在C++11之前,使用NULL(实际上是0)
    // int *nullPtr = NULL;

    if (nullPtr == nullptr) {
        cout << "这是一个空指针" << endl;
    }

    // 野指针(未初始化的指针)是危险的
    // int *wildPtr;  // 野指针,指向不确定的内存地址
    // *wildPtr = 10; // 可能导致程序崩溃

    return 0;
}

重要概念

  • &:取地址运算符,获取变量的内存地址
  • *:解引用运算符,获取指针所指向地址的值
  • 指针类型:指针的类型必须与其指向的变量类型一致(void指针除外)

5.1.2 指针运算

// 示例5.2:指针运算
#include <iostream>
using namespace std;

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *ptr = arr;  // 数组名就是数组首元素的地址

    cout << "数组元素地址和值:" << endl;
    for (int i = 0; i < 5; i++) {
        cout << "地址: " << (ptr + i) << ", 值: " << *(ptr + i) << endl;
    }

    // 指针加法
    ptr = arr;  // 重新指向数组开头
    cout << "\n指针加法:" << endl;
    cout << "ptr = " << ptr << endl;
    cout << "ptr + 1 = " << ptr + 1 << endl;  // 移动4个字节(int的大小)
    cout << "*(ptr + 1) = " << *(ptr + 1) << endl;

    // 指针减法
    int *ptr2 = &arr[4];
    cout << "\n指针减法:" << endl;
    cout << "ptr2 = " << ptr2 << endl;
    cout << "ptr2 - 1 = " << ptr2 - 1 << endl;
    cout << "*(ptr2 - 1) = " << *(ptr2 - 1) << endl;

    // 指针差值(同一数组内)
    int diff = ptr2 - ptr;
    cout << "\n指针差值(元素个数): " << diff << endl;

    // 指针比较
    if (ptr < ptr2) {
        cout << "ptr 在 ptr2 之前" << endl;
    }

    // 递增和递减
    ptr = arr;
    cout << "\n指针递增:" << endl;
    cout << "初始: *ptr = " << *ptr << endl;
    ptr++;
    cout << "递增后: *ptr = " << *ptr << endl;

    // 注意:指针运算的步长取决于指针指向的类型
    char charArr[] = {'a', 'b', 'c', 'd'};
    char *charPtr = charArr;
    cout << "\nchar指针运算:" << endl;
    cout << "charPtr = " << (void*)charPtr << endl;  // 强制转换为void*以打印地址
    cout << "charPtr + 1 = " << (void*)(charPtr + 1) << endl;  // 移动1个字节

    return 0;
}

易错点提示

  1. 指针运算不是数学运算
   int arr[5];
   int *p1 = &arr[1];
   int *p2 = &arr[4];

   // 错误理解:p2 - p1 = 3(地址差值)
   // 正确理解:p2 - p1 = 3(元素个数差)

   // 指针运算的单位是指针指向类型的大小
   cout << p2 - p1 << endl;  // 输出3,不是地址的字节差
  1. 指针比较的局限性
   int a = 10, b = 20;
   int *p1 = &a;
   int *p2 = &b;

   // 比较两个不相关变量的指针是未定义行为
   // if (p1 < p2)  // 结果不确定

5.1.3 指针常量与常量指针

这是一个容易混淆的概念,需要特别注意。

// 示例5.3:指针常量与常量指针
#include <iostream>
using namespace std;

int main() {
    int a = 10, b = 20;

    // 1. 常量指针(pointer to constant)
    // 指针指向的内容是常量,不能通过指针修改,但指针本身可以指向其他地址
    const int *ptr1 = &a;  // 或者 int const *ptr1 = &a;
    // *ptr1 = 30;  // 错误!不能通过ptr1修改a的值
    a = 30;        // 正确,a本身不是常量,可以直接修改
    ptr1 = &b;     // 正确,指针本身可以指向其他变量
    cout << "*ptr1 = " << *ptr1 << endl;

    // 2. 指针常量(constant pointer)
    // 指针本身是常量,不能指向其他地址,但可以通过指针修改指向的内容
    int *const ptr2 = &a;
    *ptr2 = 40;    // 正确,可以通过ptr2修改a的值
    // ptr2 = &b;  // 错误!指针本身是常量,不能指向其他变量

    // 3. 指向常量的指针常量
    const int *const ptr3 = &a;
    // *ptr3 = 50;  // 错误!不能通过ptr3修改a的值
    // ptr3 = &b;   // 错误!指针本身是常量

    // 记忆技巧:
    // const在*左边,表示指向的内容是常量(常量指针)
    // const在*右边,表示指针本身是常量(指针常量)
    // const在*两边都有,表示两者都是常量

    return 0;
}

记忆口诀

  • “左定值,右定向”:const在左边,值不能改;const在右边,指向不能改
  • “常量指针”指向常量,”指针常量”是常量指针

5.2 高级指针

5.2.1 指向指针的指针(二级指针)

// 示例5.4:二级指针
#include <iostream>
using namespace std;

int main() {
    int var = 100;
    int *ptr = &var;      // 一级指针,指向int
    int **pptr = &ptr;    // 二级指针,指向int*

    cout << "var = " << var << endl;
    cout << "*ptr = " << *ptr << endl;
    cout << "**pptr = " << **pptr << endl;

    // 通过二级指针修改变量的值
    **pptr = 200;
    cout << "修改后,var = " << var << endl;

    // 二级指针在动态二维数组中的应用
    int rows = 3, cols = 4;
    int **matrix = new int*[rows];  // 分配一个指针数组
    for (int i = 0; i < rows; i++) {
        matrix[i] = new int[cols];  // 为每一行分配数组
    }

    // 初始化二维数组
    int count = 1;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            matrix[i][j] = count++;
        }
    }

    // 打印二维数组
    cout << "\n动态二维数组:" << endl;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            cout << matrix[i][j] << "\t";
        }
        cout << endl;
    }

    // 释放内存
    for (int i = 0; i < rows; i++) {
        delete[] matrix[i];
    }
    delete[] matrix;

    return 0;
}

5.2.2 指针数组与数组指针

// 示例5.5:指针数组与数组指针
#include <iostream>
using namespace std;

int main() {
    // 指针数组:一个数组,其元素都是指针
    int a = 10, b = 20, c = 30;
    int *ptrArr[3];  // 指针数组,包含3个int*类型的元素

    ptrArr[0] = &a;
    ptrArr[1] = &b;
    ptrArr[2] = &c;

    cout << "指针数组:" << endl;
    for (int i = 0; i < 3; i++) {
        cout << "*ptrArr[" << i << "] = " << *ptrArr[i] << endl;
    }

    // 数组指针:一个指针,指向一个数组
    int arr[5] = {1, 2, 3, 4, 5};
    int (*arrPtr)[5];  // 数组指针,指向一个包含5个int的数组

    arrPtr = &arr;  // 取整个数组的地址

    cout << "\n数组指针:" << endl;
    for (int i = 0; i < 5; i++) {
        cout << "(*arrPtr)[" << i << "] = " << (*arrPtr)[i] << endl;
    }

    // 数组指针与二维数组
    int matrix[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    int (*rowPtr)[4];  // 指向包含4个int的数组的指针
    rowPtr = matrix;   // 二维数组名转换为指向第一行的指针

    cout << "\n通过数组指针访问二维数组:" << endl;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            cout << rowPtr[i][j] << "\t";
        }
        cout << endl;
    }

    return 0;
}

重要区别

  • 指针数组int *arr[5] – arr是数组,每个元素是int*
  • 数组指针int (*arr)[5] – arr是指针,指向包含5个int的数组

5.2.3 动态内存分配

// 示例5.6:动态内存分配
#include <iostream>
using namespace std;

int main() {
    // 1. 使用new和delete分配单个对象
    int *p = new int;     // 分配一个int
    *p = 100;
    cout << "*p = " << *p << endl;
    delete p;             // 释放内存

    // 2. 使用new[]和delete[]分配数组
    int n;
    cout << "请输入数组大小: ";
    cin >> n;

    int *arr = new int[n];  // 分配n个int的数组

    cout << "请输入" << n << "个整数: ";
    for (int i = 0; i < n; i++) {
        cin >> arr[i];
    }

    cout << "数组元素: ";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;

    delete[] arr;  // 释放数组内存

    // 3. 动态分配结构体
    struct Point {
        int x, y;
    };

    Point *pt = new Point;
    pt->x = 10;
    pt->y = 20;
    cout << "Point: (" << pt->x << ", " << pt->y << ")" << endl;
    delete pt;

    // 4. 内存分配失败的处理
    int *bigArray = new (nothrow) int[1000000000];  // 可能分配失败
    if (bigArray == nullptr) {
        cout << "内存分配失败!" << endl;
    } else {
        delete[] bigArray;
    }

    // 5. C风格动态内存分配(malloc/free)与new/delete的区别
    // malloc/free是函数,new/delete是运算符
    // malloc/free不会调用构造函数和析构函数,new/delete会

    // 使用malloc分配内存
    int *mptr = (int*)malloc(sizeof(int));
    *mptr = 50;
    cout << "*mptr = " << *mptr << endl;
    free(mptr);

    // 使用malloc分配数组
    int *marr = (int*)malloc(n * sizeof(int));
    for (int i = 0; i < n; i++) {
        marr[i] = i * 2;
    }
    free(marr);

    return 0;
}

重要区别

  1. new/delete vs malloc/free
  • new调用构造函数,delete调用析构函数
  • malloc/free只是分配/释放内存
  • new返回类型安全的指针,malloc返回void*需要强制转换
  • new可以重载,malloc不能
  1. 配对使用
   int *p1 = new int;      delete p1;        // 正确
   int *p2 = new int[10];  delete[] p2;      // 正确
   int *p3 = new int;      delete[] p3;      // 错误!未定义行为
   int *p4 = new int[10];  delete p4;        // 错误!未定义行为

5.2.4 智能指针(C++11)

智能指针是C++11引入的,用于自动管理动态内存,避免内存泄漏。

// 示例5.7:智能指针
#include <iostream>
#include <memory>  // 智能指针头文件
using namespace std;

class MyClass {
public:
    MyClass() { cout << "MyClass构造函数" << endl; }
    ~MyClass() { cout << "MyClass析构函数" << endl; }
    void sayHello() { cout << "Hello from MyClass!" << endl; }
};

int main() {
    // 1. unique_ptr:独占所有权的智能指针
    // 同一时间只能有一个unique_ptr指向一个对象
    unique_ptr<int> up1(new int(10));
    cout << "*up1 = " << *up1 << endl;

    // unique_ptr不能复制,只能移动
    unique_ptr<int> up2 = move(up1);  // up1转移给up2,up1变为空
    if (up1) {
        cout << "up1不为空" << endl;
    } else {
        cout << "up1为空" << endl;
    }

    cout << "*up2 = " << *up2 << endl;

    // 使用make_unique(C++14)更安全
    unique_ptr<int> up3 = make_unique<int>(20);
    cout << "*up3 = " << *up3 << endl;

    // 2. shared_ptr:共享所有权的智能指针
    // 多个shared_ptr可以指向同一个对象,使用引用计数
    shared_ptr<MyClass> sp1(new MyClass());
    {
        shared_ptr<MyClass> sp2 = sp1;  // 引用计数加1
        sp1->sayHello();
        sp2->sayHello();
        cout << "引用计数: " << sp1.use_count() << endl;  // 2
    }  // sp2离开作用域,引用计数减1

    cout << "引用计数: " << sp1.use_count() << endl;  // 1

    // 使用make_shared更高效
    shared_ptr<MyClass> sp3 = make_shared<MyClass>();

    // 3. weak_ptr:弱引用,不增加引用计数
    // 用于解决shared_ptr的循环引用问题
    weak_ptr<MyClass> wp = sp3;
    cout << "weak_ptr引用计数: " << wp.use_count() << endl;

    // 使用weak_ptr访问对象,需要先转换为shared_ptr
    if (shared_ptr<MyClass> sp = wp.lock()) {
        sp->sayHello();
    } else {
        cout << "对象已被释放" << endl;
    }

    // 4. 智能指针与数组
    unique_ptr<int[]> upArray(new int[5]);
    for (int i = 0; i < 5; i++) {
        upArray[i] = i * 10;
    }

    // shared_ptr默认不支持数组,需要自定义删除器
    shared_ptr<int> spArray(new int[5], [](int *p) { delete[] p; });

    // 或者使用C++17的shared_ptr支持数组
    // shared_ptr<int[]> spArray(new int[5]);

    return 0;
}
// 注意:智能指针会在离开作用域时自动释放内存,无需手动delete

智能指针总结

  • unique_ptr:独占所有权,轻量级,性能好
  • shared_ptr:共享所有权,使用引用计数,有开销
  • weak_ptr:弱引用,配合shared_ptr使用,解决循环引用

5.3 引用

5.3.1 引用的基本概念

引用是变量的别名,必须在定义时初始化,且不能改变指向。

// 示例5.8:引用的基本概念
#include <iostream>
using namespace std;

int main() {
    int var = 10;
    int &ref = var;  // ref是var的引用(别名)

    cout << "var = " << var << endl;
    cout << "ref = " << ref << endl;

    // 通过引用修改变量的值
    ref = 20;
    cout << "修改后,var = " << var << endl;

    // 引用必须初始化
    // int &ref2;  // 错误!引用必须初始化

    // 引用一旦初始化,就不能再引用其他变量
    int another = 30;
    // ref = another;  // 这不是让ref引用another,而是将another的值赋给ref(即var)

    // 引用的引用
    int &ref2 = ref;  // ref2也是var的引用
    ref2 = 40;
    cout << "通过ref2修改后,var = " << var << endl;

    // 常量引用
    const int &cref = var;  // 常量引用,不能通过cref修改var
    // cref = 50;  // 错误!
    var = 50;  // 可以直接修改var

    // 常量引用可以绑定到常量
    const int constant = 100;
    const int &cref2 = constant;

    // 常量引用可以绑定到字面量
    const int &cref3 = 200;  // 合法

    return 0;
}

5.3.2 引用与指针对比

// 示例5.9:引用与指针对比
#include <iostream>
using namespace std;

void swapByPointer(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

void swapByReference(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 10, y = 20;

    cout << "交换前: x = " << x << ", y = " << y << endl;

    // 使用指针交换
    swapByPointer(&x, &y);
    cout << "指针交换后: x = " << x << ", y = " << y << endl;

    // 使用引用交换
    swapByReference(x, y);
    cout << "引用交换后: x = " << x << ", y = " << y << endl;

    // 引用与指针的区别:
    // 1. 引用必须初始化,指针可以不初始化(但危险)
    // 2. 引用不能改变指向,指针可以
    // 3. 引用不能为空,指针可以为空
    // 4. 引用更安全,指针更灵活
    // 5. 引用在语法上更简洁

    // 引用作为返回值
    int arr[5] = {1, 2, 3, 4, 5};
    int &getElement(int index);  // 函数声明,返回引用

    getElement(2) = 100;  // 修改数组元素

    cout << "arr[2] = " << arr[2] << endl;

    // 注意:不能返回局部变量的引用
    // int& badFunction() {
    //     int local = 10;
    //     return local;  // 错误!局部变量在函数结束后被销毁
    // }

    return 0;
}

int &getElement(int index) {
    static int arr[5] = {1, 2, 3, 4, 5};  // 静态数组,生命周期持续到程序结束
    return arr[index];
}

引用 vs 指针

特性引用指针
初始化必须初始化可以不初始化
指向不能改变指向可以改变指向
空值不能为空可以为空
语法简洁(直接使用)复杂(需要*和&)
安全性更安全更灵活但也更危险
内存占用通常不占用额外内存占用指针大小的内存

5.4 指针与引用常见考点

5.4.1 悬空指针与野指针

// 示例5.10:悬空指针与野指针
#include <iostream>
using namespace std;

int* createArray(int size) {
    int *arr = new int[size];
    for (int i = 0; i < size; i++) {
        arr[i] = i + 1;
    }
    return arr;
}

int main() {
    // 野指针:未初始化的指针
    // int *wildPtr;  // 野指针,指向不确定的内存
    // *wildPtr = 10; // 可能导致程序崩溃

    // 悬空指针:指向已被释放的内存的指针
    int *ptr = new int(100);
    cout << "*ptr = " << *ptr << endl;
    delete ptr;  // 释放内存

    // ptr现在成为悬空指针,指向的内存已被释放
    // *ptr = 200;  // 危险!访问已释放的内存

    // 避免悬空指针:释放后立即置空
    ptr = nullptr;

    // 返回局部变量的指针
    int* badPtr = createArray(5);
    // 使用badPtr...
    delete[] badPtr;  // 必须释放内存,否则内存泄漏

    // 使用智能指针避免这些问题
    unique_ptr<int[]> safePtr = make_unique<int[]>(5);
    // 无需手动释放,自动管理内存

    return 0;
}

5.4.2 内存泄漏

// 示例5.11:内存泄漏
#include <iostream>
using namespace std;

void memoryLeak() {
    int *ptr = new int(10);  // 分配内存
    // 忘记delete ptr;  // 内存泄漏!
    // 函数结束,ptr被销毁,但分配的内存没有释放
}

void noLeak() {
    int *ptr = new int(10);
    delete ptr;  // 正确释放
}

void exceptionLeak() {
    int *ptr = new int(10);
    // 如果这里抛出异常,delete可能不会执行,导致内存泄漏
    // delete ptr;
}

void safeWithSmartPointer() {
    unique_ptr<int> ptr(new int(10));
    // 即使抛出异常,智能指针也会自动释放内存
}

int main() {
    memoryLeak();  // 每次调用都会泄漏一个int的内存

    // 循环中的内存泄漏
    for (int i = 0; i < 100; i++) {
        int *ptr = new int[1000];  // 每次循环分配内存
        // 没有释放,内存泄漏累积
        // delete[] ptr;  // 应该在这里释放
    }

    return 0;
}

5.4.3 const与指针/引用

// 示例5.12:const与指针/引用
#include <iostream>
using namespace std;

int main() {
    int a = 10;
    const int b = 20;

    // 1. const与指针
    const int *p1 = &a;        // 指向const int的指针
    int const *p2 = &a;        // 同上,等价写法
    int *const p3 = &a;        // const指针,指向int
    const int *const p4 = &a;  // const指针,指向const int

    // 2. const与引用
    const int &r1 = a;         // const引用,不能通过r1修改a
    // int &const r2 = a;      // 错误!引用本身就是不可变的,不需要const

    // 3. 函数参数中的const
    // void func(const int* ptr);    // 保护指针指向的内容不被修改
    // void func(int* const ptr);    // 保护指针本身不被修改(很少用)
    // void func(const int& ref);    // 保护引用参数不被修改

    // 4. const在函数返回中的应用
    // const int* getPointer();      // 返回的指针不能用于修改其指向的内容

    // 5. 类型转换
    const int *pc = &a;
    // int *p = pc;                  // 错误!不能去掉const
    int *p = const_cast<int*>(pc);   // 使用const_cast去掉const(危险!)

    return 0;
}

综合例题与解析

// 例题1:复杂指针声明解析
#include <iostream>
using namespace std;

int main() {
    // 解析复杂指针声明的方法:从变量名开始,从内向外,从左向右

    int var = 10;

    // 1. int *p1;
    // p1是一个指针,指向int

    // 2. int **p2;
    // p2是一个指针,指向int*(二级指针)

    // 3. int arr[5];
    // arr是一个数组,包含5个int

    // 4. int *arrPtr[5];
    // arrPtr是一个数组,包含5个int*(指针数组)

    // 5. int (*ptrArr)[5];
    // ptrArr是一个指针,指向一个包含5个int的数组(数组指针)

    // 6. int (*funcPtr)(int, int);
    // funcPtr是一个指针,指向一个函数,该函数接受两个int参数,返回int

    // 7. int &ref = var;
    // ref是一个引用,引用int

    // 8. int *&refPtr = p1;
    // refPtr是一个引用,引用int*(指针的引用)

    // 9. const int *ptr1 = &var;
    // ptr1是一个指针,指向const int(常量指针)

    // 10. int *const ptr2 = &var;
    // ptr2是一个const指针,指向int(指针常量)

    // 11. const int *const ptr3 = &var;
    // ptr3是一个const指针,指向const int

    // 右左法则:从变量名开始,先向右看,再向左看
    // 例如:int (*ptrArr)[5];
    // 1. ptrArr是一个指针(*ptrArr)
    // 2. 指向一个数组((*ptrArr)[5])
    // 3. 数组元素是int

    return 0;
}
// 例题2:实现字符串操作函数
#include <iostream>
#include <cstring>
using namespace std;

// 使用指针实现字符串操作
int myStrlen(const char *str) {
    const char *p = str;
    while (*p != '\0') {
        p++;
    }
    return p - str;
}

void myStrcpy(char *dest, const char *src) {
    while ((*dest++ = *src++) != '\0');
}

int myStrcmp(const char *str1, const char *str2) {
    while (*str1 && (*str1 == *str2)) {
        str1++;
        str2++;
    }
    return *(unsigned char*)str1 - *(unsigned char*)str2;
}

void myStrcat(char *dest, const char *src) {
    // 移动到dest的末尾
    while (*dest) dest++;
    // 复制src
    while ((*dest++ = *src++) != '\0');
}

int main() {
    char str1[100] = "Hello";
    char str2[100] = "World";

    cout << "str1长度: " << myStrlen(str1) << endl;

    myStrcpy(str1, str2);
    cout << "复制后str1: " << str1 << endl;

    cout << "比较str1和str2: " << myStrcmp(str1, str2) << endl;

    myStrcat(str1, "!!!");
    cout << "连接后str1: " << str1 << endl;

    return 0;
}
// 例题3:动态数组类(模拟vector)
#include <iostream>
using namespace std;

class DynamicArray {
private:
    int *data;
    int size;
    int capacity;

    void resize(int newCapacity) {
        int *newData = new int[newCapacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        delete[] data;
        data = newData;
        capacity = newCapacity;
    }

public:
    DynamicArray(int initialCapacity = 10) {
        capacity = initialCapacity;
        size = 0;
        data = new int[capacity];
    }

    ~DynamicArray() {
        delete[] data;
    }

    // 拷贝构造函数(深拷贝)
    DynamicArray(const DynamicArray &other) {
        capacity = other.capacity;
        size = other.size;
        data = new int[capacity];
        for (int i = 0; i < size; i++) {
            data[i] = other.data[i];
        }
    }

    // 拷贝赋值运算符(深拷贝)
    DynamicArray& operator=(const DynamicArray &other) {
        if (this != &other) {
            delete[] data;
            capacity = other.capacity;
            size = other.size;
            data = new int[capacity];
            for (int i = 0; i < size; i++) {
                data[i] = other.data[i];
            }
        }
        return *this;
    }

    void push_back(int value) {
        if (size == capacity) {
            resize(capacity * 2);
        }
        data[size++] = value;
    }

    int& operator[](int index) {
        if (index < 0 || index >= size) {
            throw out_of_range("索引越界");
        }
        return data[index];
    }

    const int& operator[](int index) const {
        if (index < 0 || index >= size) {
            throw out_of_range("索引越界");
        }
        return data[index];
    }

    int getSize() const { return size; }
    int getCapacity() const { return capacity; }

    void print() const {
        for (int i = 0; i < size; i++) {
            cout << data[i] << " ";
        }
        cout << endl;
    }
};

int main() {
    DynamicArray arr;

    for (int i = 0; i < 20; i++) {
        arr.push_back(i * 2);
    }

    cout << "数组元素: ";
    arr.print();

    cout << "大小: " << arr.getSize() << endl;
    cout << "容量: " << arr.getCapacity() << endl;

    // 测试拷贝
    DynamicArray arr2 = arr;
    arr2[0] = 100;

    cout << "原数组: ";
    arr.print();
    cout << "拷贝数组: ";
    arr2.print();

    return 0;
}

本章小结

重点回顾

  1. 指针基础:指针的概念、运算、常量指针与指针常量
  2. 高级指针:二级指针、指针数组、数组指针、动态内存分配
  3. 智能指针:unique_ptr、shared_ptr、weak_ptr的使用
  4. 引用:引用的概念、与指针对比、引用传递
  5. 常见问题:悬空指针、野指针、内存泄漏

易错易混淆点

  1. 指针与引用的区别:引用必须初始化、不能为空、不能改变指向
  2. 指针常量与常量指针:const在*左边还是右边
  3. 数组指针与指针数组int (*ptr)[5] vs int *ptr[5]
  4. 动态内存管理:new/delete必须配对,避免内存泄漏
  5. 智能指针所有权:unique_ptr独占,shared_ptr共享

考研笔试常见题型

  1. 选择题:考察指针和引用的基本概念和区别
  2. 程序阅读题:分析指针运算、动态内存管理的代码
  3. 程序填空题:补全指针操作、内存分配的代码
  4. 编程题:实现字符串操作、动态数据结构等

学习建议

  1. 多画内存图:理解指针指向和内存布局
  2. 多写代码:实践指针和引用的各种用法
  3. 使用智能指针:现代C++中优先使用智能指针
  4. 注意安全:避免悬空指针、野指针和内存泄漏

6. 类与对象(面向对象)

├── 6.1 类的基本概念
│   ├── 类定义
│   ├── 对象创建与使用
│   ├── 访问控制
│   │   ├── public
│   │   ├── private
│   │   └── protected
│   └── 类与结构体对比
├── 6.2 成员函数
│   ├── 成员函数定义
│   ├── const成员函数
│   ├── 内联成员函数
│   ├── 静态成员函数
│   └── 友元函数
├── 6.3 构造函数与析构函数
│   ├── 构造函数
│   │   ├── 默认构造函数
│   │   ├── 带参构造函数
│   │   ├── 拷贝构造函数(深/浅拷贝)
│   │   ├── 委托构造函数(C++11)
│   │   └── 转换构造函数
│   ├── 析构函数
│   ├── 构造函数初始化列表
│   └── explicit关键字
├── 6.4 特殊成员
│   ├── 静态成员变量
│   ├── const成员变量
│   ├── mutable成员变量
│   └── 友元类
├── 6.5 this指针
│   └── this指针的应用
└── 6.6 对象数组与对象指针

类是C++面向对象编程的核心,理解类的概念和用法是掌握C++的关键。本章将深入讲解类与对象的所有重要概念。

6.1 类的基本概念

6.1.1 类与结构体的区别

// 示例6.1:类的基本概念
#include <iostream>
#include <string>
using namespace std;

// 1. 使用class关键字定义类
class Student {
// 访问控制修饰符
private:  // 私有成员,只能被类内部的成员函数访问
    string name;
    int age;
    float score;

public:   // 公有成员,可以在类外部访问
    // 成员函数(方法)
    void setInfo(string n, int a, float s) {
        name = n;
        age = a;
        score = s;
    }

    void display() {
        cout << "姓名: " << name << endl;
        cout << "年龄: " << age << endl;
        cout << "成绩: " << score << endl;
    }

    // 内联成员函数
    float getScore() { return score; }

    // 常量成员函数:不会修改对象的状态
    void printInfo() const {
        // name = "张三";  // 错误!const成员函数不能修改成员变量
        cout << "姓名: " << name << ", 成绩: " << score << endl;
    }
};

// 2. 使用struct关键字定义类(默认成员是public)
struct Point {
    // struct成员默认是public
    int x;
    int y;

    void set(int xVal, int yVal) {
        x = xVal;
        y = yVal;
    }
};

int main() {
    // 创建对象
    Student stu1;  // 栈上分配

    // 访问公有成员函数
    stu1.setInfo("张三", 20, 85.5);
    stu1.display();

    // 不能直接访问私有成员
    // stu1.name = "李四";  // 错误!name是私有成员

    // 使用struct创建对象
    Point p1;
    p1.x = 10;  // struct成员默认public,可以直接访问
    p1.y = 20;
    cout << "点坐标: (" << p1.x << ", " << p1.y << ")" << endl;

    // 3. 动态创建对象
    Student* stu2 = new Student();  // 堆上分配
    stu2->setInfo("李四", 22, 90.0);
    stu2->display();

    delete stu2;  // 必须手动释放

    return 0;
}

重要概念

  1. class vs struct
  • class默认成员是private,struct默认成员是public
  • 除此之外,两者在C++中几乎没有区别
  • 习惯上:class用于数据+操作,struct用于纯数据
  1. 访问控制
  • private:只能被类的成员函数访问
  • public:可以被任何代码访问
  • protected:在继承中使用,后面会讲

6.1.2 类的封装

// 示例6.2:类的封装
#include <iostream>
#include <string>
using namespace std;

// 封装的银行账户类
class BankAccount {
private:
    string accountNumber;  // 账号
    string ownerName;      // 户主姓名
    double balance;        // 余额

    // 私有辅助函数
    bool isValidAmount(double amount) {
        return amount > 0;
    }

public:
    // 设置账户信息
    void setAccount(string accNum, string owner, double initialBalance) {
        if (initialBalance < 0) {
            cout << "初始余额不能为负数!" << endl;
            return;
        }
        accountNumber = accNum;
        ownerName = owner;
        balance = initialBalance;
    }

    // 存款
    bool deposit(double amount) {
        if (!isValidAmount(amount)) {
            cout << "存款金额必须为正数!" << endl;
            return false;
        }
        balance += amount;
        cout << "成功存款 " << amount << " 元" << endl;
        return true;
    }

    // 取款
    bool withdraw(double amount) {
        if (!isValidAmount(amount)) {
            cout << "取款金额必须为正数!" << endl;
            return false;
        }

        if (amount > balance) {
            cout << "余额不足!" << endl;
            return false;
        }

        balance -= amount;
        cout << "成功取款 " << amount << " 元" << endl;
        return true;
    }

    // 获取余额(只读)
    double getBalance() const {
        return balance;
    }

    // 显示账户信息
    void displayAccount() const {
        cout << "账户信息:" << endl;
        cout << "账号: " << accountNumber << endl;
        cout << "户主: " << ownerName << endl;
        cout << "余额: " << balance << " 元" << endl;
    }
};

int main() {
    BankAccount account;

    // 初始化账户
    account.setAccount("6228480010123456789", "张三", 1000.0);

    // 存款和取款
    account.deposit(500.0);
    account.withdraw(200.0);

    // 尝试取款超过余额
    account.withdraw(2000.0);

    // 显示账户信息
    account.displayAccount();

    // 不能直接访问私有成员
    // account.balance = 1000000;  // 错误!

    return 0;
}

封装的好处

  1. 数据保护:防止外部代码直接修改数据
  2. 接口稳定:可以修改内部实现而不影响外部代码
  3. 易于维护:数据验证和业务逻辑集中在类内部

6.2 构造函数与析构函数

6.2.1 构造函数

// 示例6.3:构造函数
#include <iostream>
#include <string>
using namespace std;

class Person {
private:
    string name;
    int age;
    string gender;

public:
    // 1. 默认构造函数(无参数)
    Person() {
        name = "未知";
        age = 0;
        gender = "未知";
        cout << "默认构造函数被调用" << endl;
    }

    // 2. 带参数的构造函数
    Person(string n, int a, string g) {
        name = n;
        age = a;
        gender = g;
        cout << "带参数的构造函数被调用" << endl;
    }

    // 3. 构造函数初始化列表(推荐方式)
    Person(string n, int a) : name(n), age(a), gender("未知") {
        // 成员初始化已经在初始化列表中完成
        cout << "使用初始化列表的构造函数被调用" << endl;
    }

    // 4. 拷贝构造函数
    Person(const Person& other) {
        name = other.name;
        age = other.age;
        gender = other.gender;
        cout << "拷贝构造函数被调用" << endl;
    }

    // 5. 委托构造函数(C++11)
    Person(string n) : Person(n, 0, "未知") {
        cout << "委托构造函数被调用" << endl;
    }

    // 成员函数
    void display() const {
        cout << "姓名: " << name << ", 年龄: " << age << ", 性别: " << gender << endl;
    }

    // 设置年龄,包含数据验证
    void setAge(int a) {
        if (a >= 0 && a <= 150) {
            age = a;
        } else {
            cout << "无效的年龄!" << endl;
        }
    }
};

int main() {
    cout << "=== 构造函数演示 ===" << endl;

    // 调用默认构造函数
    Person p1;
    p1.display();

    // 调用带参数的构造函数
    Person p2("张三", 25, "男");
    p2.display();

    // 调用使用初始化列表的构造函数
    Person p3("李四", 30);
    p3.display();

    // 调用拷贝构造函数
    Person p4 = p2;  // 等价于 Person p4(p2);
    p4.display();

    // 调用委托构造函数
    Person p5("王五");
    p5.display();

    // 动态创建对象
    Person* p6 = new Person("赵六", 35, "男");
    p6->display();
    delete p6;

    // 2. 构造函数与explicit关键字
    cout << "\n=== explicit关键字演示 ===" << endl;

    class Test {
        int value;
    public:
        // 没有explicit,允许隐式转换
        Test(int v) : value(v) {
            cout << "Test构造函数: " << value << endl;
        }
    };

    Test t1 = 100;  // 隐式转换:100 -> Test(100)

    class TestExplicit {
        int value;
    public:
        // 使用explicit,禁止隐式转换
        explicit TestExplicit(int v) : value(v) {
            cout << "TestExplicit构造函数: " << value << endl;
        }
    };

    TestExplicit t2(200);  // 正确,显式调用
    // TestExplicit t3 = 300;  // 错误!不能隐式转换

    return 0;
}

构造函数的重要特点

  1. 与类同名,没有返回类型
  2. 可以重载:可以有多个构造函数
  3. 创建对象时自动调用,不能显式调用
  4. 初始化列表:更高效,某些情况必须使用

6.2.2 析构函数

// 示例6.4:析构函数
#include <iostream>
#include <cstring>
using namespace std;

class String {
private:
    char* data;
    int length;

public:
    // 构造函数
    String(const char* str = "") {
        length = strlen(str);
        data = new char[length + 1];  // +1 用于存储'\0'
        strcpy(data, str);
        cout << "构造函数: " << data << endl;
    }

    // 拷贝构造函数(深拷贝)
    String(const String& other) {
        length = other.length;
        data = new char[length + 1];
        strcpy(data, other.data);
        cout << "拷贝构造函数: " << data << endl;
    }

    // 析构函数
    ~String() {
        cout << "析构函数: " << data << endl;
        delete[] data;  // 释放动态分配的内存
    }

    // 显示字符串
    void display() const {
        cout << "字符串: " << data << " (长度: " << length << ")" << endl;
    }

    // 获取长度
    int getLength() const { return length; }
};

void testFunction() {
    cout << "\n进入testFunction..." << endl;
    String localStr("局部字符串");
    localStr.display();
    cout << "离开testFunction..." << endl;
    // localStr的析构函数会被自动调用
}

int main() {
    cout << "=== 析构函数演示 ===" << endl;

    // 1. 栈上对象的析构
    {
        cout << "\n进入代码块..." << endl;
        String str1("Hello");
        str1.display();
        cout << "离开代码块..." << endl;
        // str1的析构函数会被调用
    }

    // 2. 函数中对象的析构
    testFunction();

    // 3. 动态分配对象的析构
    cout << "\n动态分配对象..." << endl;
    String* pStr = new String("动态字符串");
    pStr->display();
    delete pStr;  // 必须手动调用delete,否则析构函数不会被调用

    // 4. 对象数组的析构
    cout << "\n对象数组..." << endl;
    String arr[3] = {String("第一个"), String("第二个"), String("第三个")};

    // 5. 深拷贝与浅拷贝问题
    cout << "\n=== 深拷贝与浅拷贝 ===" << endl;

    String s1("原始字符串");

    // 如果没有定义拷贝构造函数,编译器会生成默认的拷贝构造函数(浅拷贝)
    // 浅拷贝:只复制指针,不复制指向的内容
    // 深拷贝:复制指针指向的内容

    String s2 = s1;  // 使用我们定义的拷贝构造函数(深拷贝)
    s2.display();

    return 0;
    // main函数结束时,所有栈上对象的析构函数会被调用
}

析构函数的重要特点

  1. ~类名(),没有参数,没有返回类型
  2. 对象销毁时自动调用,不能显式调用
  3. 用于释放资源:如动态内存、文件句柄、网络连接等
  4. 可以虚析构函数:用于多态,后面会讲

6.2.3 构造函数初始化列表

// 示例6.5:构造函数初始化列表
#include <iostream>
using namespace std;

class Point {
private:
    const int id;       // const成员,必须在初始化列表中初始化
    int& ref;           // 引用成员,必须在初始化列表中初始化
    int x;
    int y;

public:
    // 初始化列表是唯一初始化const和引用成员的地方
    Point(int i, int& r, int xVal, int yVal) 
        : id(i), ref(r), x(xVal), y(yVal) {
        // 构造函数体
        cout << "Point构造函数" << endl;
    }

    // 错误:不能在构造函数体内初始化const和引用成员
    // Point(int i, int& r, int xVal, int yVal) {
    //     id = i;  // 错误!const成员不能赋值
    //     ref = r; // 错误!引用必须在定义时初始化
    //     x = xVal;
    //     y = yVal;
    // }

    void display() const {
        cout << "ID: " << id << ", 引用: " << ref 
             << ", 坐标: (" << x << ", " << y << ")" << endl;
    }

    // 初始化列表中的初始化顺序
    // 注意:初始化顺序只与成员声明的顺序有关,与初始化列表中的顺序无关
    // 以下示例展示初始化顺序问题
    class BadExample {
        int a;
        int b;
    public:
        // 虽然初始化列表中b在a前面,但实际初始化顺序是a先于b
        BadExample(int val) : b(val), a(b * 2) {  // 危险!a使用未初始化的b
            cout << "a = " << a << ", b = " << b << endl;
        }
    };

    // 正确做法:按声明顺序初始化
    class GoodExample {
        int a;
        int b;
    public:
        GoodExample(int val) : a(val * 2), b(val) {  // 正确
            cout << "a = " << a << ", b = " << b << endl;
        }
    };
};

class Complex {
private:
    double real;
    double imag;

public:
    // 多个构造函数使用初始化列表
    Complex() : real(0), imag(0) {}  // 默认构造函数

    Complex(double r) : real(r), imag(0) {}  // 转换构造函数

    Complex(double r, double i) : real(r), imag(i) {}  // 带两个参数

    // 使用初始化列表调用成员对象的构造函数
    class Line {
        Point start;
        Point end;
    public:
        Line(const Point& s, const Point& e) 
            : start(s), end(e) {  // 调用Point的拷贝构造函数
            cout << "Line构造函数" << endl;
        }
    };

    void display() const {
        cout << real << " + " << imag << "i" << endl;
    }
};

int main() {
    cout << "=== 构造函数初始化列表演示 ===" << endl;

    int value = 10;
    Point p(1, value, 3, 4);
    p.display();

    // 修改引用的原始值
    value = 20;
    p.display();  // ref也会改变

    // 初始化顺序问题演示
    cout << "\n初始化顺序问题:" << endl;
    cout << "BadExample:" << endl;
    Point::BadExample bad(10);  // 输出未定义的值

    cout << "\nGoodExample:" << endl;
    Point::GoodExample good(10);  // 输出正确的值

    // Complex类演示
    cout << "\nComplex类演示:" << endl;
    Complex c1;
    Complex c2(3.5);
    Complex c3(2.0, 4.5);

    c1.display();
    c2.display();
    c3.display();

    return 0;
}

初始化列表的重要规则

  1. 必须使用初始化列表
  • const成员
  • 引用成员
  • 没有默认构造函数的类类型成员
  1. 初始化顺序
  • 按照成员声明的顺序初始化
  • 与初始化列表中的顺序无关
  1. 初始化列表的优势
  • 效率更高(避免先默认构造再赋值)
  • 某些情况必须使用

6.3 特殊成员

6.3.1 静态成员

// 示例6.6:静态成员
#include <iostream>
using namespace std;

class Student {
private:
    string name;
    int score;

    // 静态成员变量:属于类,不属于任何一个对象
    static int totalStudents;    // 静态成员变量声明
    static float totalScore;     // 所有学生的总成绩

public:
    Student(string n, int s) : name(n), score(s) {
        totalStudents++;         // 每创建一个学生,计数加1
        totalScore += score;     // 累加成绩
    }

    ~Student() {
        totalStudents--;         // 学生对象销毁时计数减1
        totalScore -= score;     // 减去成绩
    }

    // 静态成员函数:只能访问静态成员
    static int getTotalStudents() {
        // name = "test";  // 错误!不能访问非静态成员
        return totalStudents;
    }

    static float getAverageScore() {
        if (totalStudents == 0) return 0;
        return totalScore / totalStudents;
    }

    // 显示学生信息
    void display() const {
        cout << "姓名: " << name << ", 成绩: " << score << endl;
    }

    // 静态常量成员
    static const int MAX_SCORE = 100;  // 可以在类内初始化
    static const int MIN_SCORE;        // 也可以在类外初始化
};

// 静态成员变量必须在类外定义和初始化
int Student::totalStudents = 0;      // 定义并初始化
float Student::totalScore = 0.0f;    // 定义并初始化
const int Student::MIN_SCORE = 0;    // 静态常量成员在类外初始化

// 静态成员的应用:单例模式
class Singleton {
private:
    static Singleton* instance;  // 静态成员,指向唯一实例
    int data;

    // 私有构造函数,防止外部创建对象
    Singleton() : data(0) {
        cout << "Singleton创建" << endl;
    }

    // 私有拷贝构造函数和赋值运算符,防止拷贝
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

public:
    // 静态成员函数获取唯一实例
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }

    // 静态成员函数释放实例
    static void destroyInstance() {
        if (instance != nullptr) {
            delete instance;
            instance = nullptr;
        }
    }

    void setData(int value) { data = value; }
    int getData() const { return data; }

    void display() const {
        cout << "Singleton data: " << data << endl;
    }
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;

// 静态成员在模板类中的应用
template<typename T>
class Container {
private:
    T value;

public:
    // 每个模板实例化都有自己的静态成员
    static int count;  // 每种类型的Container有自己的count

    Container(T v) : value(v) {
        count++;
    }

    ~Container() {
        count--;
    }

    static int getCount() {
        return count;
    }

    T getValue() const { return value; }
};

// 模板类的静态成员初始化
template<typename T>
int Container<T>::count = 0;

int main() {
    cout << "=== 静态成员演示 ===" << endl;

    // 访问静态成员
    cout << "最大成绩: " << Student::MAX_SCORE << endl;
    cout << "最小成绩: " << Student::MIN_SCORE << endl;
    cout << "初始学生数: " << Student::getTotalStudents() << endl;

    // 创建学生对象
    Student s1("张三", 85);
    Student s2("李四", 92);
    Student s3("王五", 78);

    s1.display();
    s2.display();
    s3.display();

    // 通过对象访问静态成员函数
    cout << "\n当前学生数: " << s1.getTotalStudents() << endl;
    cout << "平均成绩: " << Student::getAverageScore() << endl;

    // 通过类名访问静态成员函数
    cout << "通过类名访问 - 学生数: " << Student::getTotalStudents() << endl;

    // 销毁一个学生
    {
        Student s4("赵六", 88);
        cout << "创建赵六后学生数: " << Student::getTotalStudents() << endl;
    }  // s4离开作用域,析构函数被调用

    cout << "赵六销毁后学生数: " << Student::getTotalStudents() << endl;

    // 单例模式演示
    cout << "\n=== 单例模式演示 ===" << endl;

    Singleton* singleton = Singleton::getInstance();
    singleton->setData(100);
    singleton->display();

    // 再次获取实例,应该是同一个
    Singleton* sameInstance = Singleton::getInstance();
    sameInstance->setData(200);
    singleton->display();  // data变为200

    // 释放实例
    Singleton::destroyInstance();

    // 模板类的静态成员演示
    cout << "\n=== 模板类的静态成员 ===" << endl;

    Container<int> intContainer1(10);
    Container<int> intContainer2(20);

    Container<double> doubleContainer1(3.14);
    Container<double> doubleContainer2(2.71);

    cout << "int Container数量: " << Container<int>::getCount() << endl;
    cout << "double Container数量: " << Container<double>::getCount() << endl;

    return 0;
}

静态成员的特点

  1. 属于类,不属于对象:所有对象共享同一份静态成员
  2. 必须在类外定义和初始化(除了静态常量整数成员)
  3. 静态成员函数
  • 不能访问非静态成员
  • 不能使用this指针
  • 可以通过对象或类名调用

6.3.2 const和mutable成员

// 示例6.7:const和mutable成员
#include <iostream>
using namespace std;

class Account {
private:
    string owner;
    mutable double balance;  // mutable成员,即使在const成员函数中也可以修改
    const int accountId;     // const成员,创建后不能修改
    double interestRate;

public:
    Account(string o, int id, double initial)
        : owner(o), accountId(id), balance(initial), interestRate(0.03) {
    }

    // const成员函数:承诺不修改对象状态
    void display() const {
        cout << "账户ID: " << accountId << endl;
        cout << "户主: " << owner << endl;
        cout << "余额: " << balance << endl;
        // owner = "test";  // 错误!const成员函数不能修改成员
    }

    // 非const成员函数
    void deposit(double amount) {
        balance += amount;
        cout << "存款 " << amount << " 成功" << endl;
    }

    // 重载:const和非const版本
    // const版本
    double getBalance() const {
        cout << "调用const版本的getBalance" << endl;
        return balance;
    }

    // 非const版本
    double getBalance() {
        cout << "调用非const版本的getBalance" << endl;
        return balance;
    }

    // mutable成员可以在const函数中修改
    void addInterest() const {
        // 虽然这是const函数,但可以修改mutable成员
        double interest = balance * interestRate;
        balance += interest;  // 可以修改mutable成员
        cout << "添加利息: " << interest << endl;
    }

    // 不能修改const成员
    // void changeId(int newId) {
    //     accountId = newId;  // 错误!const成员不能修改
    // }
};

// const对象
void processConstAccount(const Account& acc) {
    // acc是const引用,只能调用const成员函数
    acc.display();
    cout << "余额: " << acc.getBalance() << endl;  // 调用const版本

    // acc.deposit(100);  // 错误!不能调用非const成员函数

    acc.addInterest();  // 可以调用,虽然它修改了mutable成员
}

int main() {
    cout << "=== const和mutable成员演示 ===" << endl;

    Account acc("张三", 1001, 1000.0);

    // 调用非const版本的getBalance
    cout << "初始余额: " << acc.getBalance() << endl;

    // 存款
    acc.deposit(500.0);

    // 处理const引用
    cout << "\n处理const引用:" << endl;
    processConstAccount(acc);

    // 调用const版本的getBalance
    cout << "\n通过const对象调用:" << endl;
    const Account& constRef = acc;
    cout << "余额: " << constRef.getBalance() << endl;  // 调用const版本

    // const对象
    const Account constAcc("李四", 1002, 2000.0);
    constAcc.display();
    // constAcc.deposit(100);  // 错误!const对象不能调用非const成员函数

    return 0;
}

const成员的重要规则

  1. const成员函数
  • 不能修改成员变量(mutable除外)
  • 只能调用其他const成员函数
  • const对象只能调用const成员函数
  1. mutable成员
  • 可以在const成员函数中被修改
  • 用于表示与对象逻辑状态无关的物理状态
  1. 函数重载
  • const和非const版本的成员函数可以重载
  • 根据对象的const性质调用相应版本

6.3.3 友元

// 示例6.8:友元
#include <iostream>
#include <cmath>
using namespace std;

class Point;  // 前向声明

// 友元函数:计算两点距离
double distance(const Point& p1, const Point& p2);

class Point {
private:
    double x, y;

    // 私有辅助函数
    double square(double val) const {
        return val * val;
    }

public:
    Point(double xVal = 0, double yVal = 0) : x(xVal), y(yVal) {}

    // 友元函数声明
    friend double distance(const Point& p1, const Point& p2);

    // 友元类声明
    friend class PointTransformer;

    // 友元成员函数
    friend void displayPoint(const Point& p);

    // 普通成员函数
    void display() const {
        cout << "(" << x << ", " << y << ")";
    }

    // 获取私有成员(通常不需要友元,用getter即可)
    double getX() const { return x; }
    double getY() const { return y; }
};

// 友元类
class PointTransformer {
public:
    // 可以访问Point的私有成员
    static void translate(Point& p, double dx, double dy) {
        p.x += dx;  // 直接访问私有成员
        p.y += dy;
    }

    static void rotate(Point& p, double angle) {
        double newX = p.x * cos(angle) - p.y * sin(angle);
        double newY = p.x * sin(angle) + p.y * cos(angle);
        p.x = newX;
        p.y = newY;
    }
};

// 友元函数定义
double distance(const Point& p1, const Point& p2) {
    // 可以直接访问Point的私有成员
    return sqrt(p1.square(p1.x - p2.x) + p1.square(p1.y - p2.y));
}

// 友元成员函数
void displayPoint(const Point& p) {
    // 可以访问Point的私有成员
    cout << "点坐标: (" << p.x << ", " << p.y << ")" << endl;
}

// 非友元函数,不能访问私有成员
// double badDistance(const Point& p1, const Point& p2) {
//     return sqrt((p1.x - p2.x)*(p1.x - p2.x) + 
//                 (p1.y - p2.y)*(p1.y - p2.y));  // 错误!不能访问私有成员
// }

int main() {
    cout << "=== 友元演示 ===" << endl;

    Point p1(3, 4);
    Point p2(0, 0);

    cout << "点p1: ";
    p1.display();
    cout << endl;

    cout << "点p2: ";
    p2.display();
    cout << endl;

    // 使用友元函数计算距离
    cout << "两点距离: " << distance(p1, p2) << endl;

    // 使用友元成员函数
    displayPoint(p1);

    // 使用友元类
    cout << "\n平移点p1 (dx=2, dy=3):" << endl;
    PointTransformer::translate(p1, 2, 3);
    p1.display();
    cout << endl;

    cout << "\n旋转点p1 90度:" << endl;
    PointTransformer::rotate(p1, 3.14159/2);  // 90度
    p1.display();
    cout << endl;

    // 通过getter访问私有成员
    cout << "\n通过getter访问:" << endl;
    cout << "p1.x = " << p1.getX() << ", p1.y = " << p1.getY() << endl;

    return 0;
}

友元的重要规则

  1. 友元不是成员:友元函数或友元类不是类的成员
  2. 破坏封装性:友元可以访问类的私有成员,谨慎使用
  3. 单向性:友元关系不是双向的,也不是传递的
  4. 使用场景
  • 运算符重载(特别是需要访问私有成员的运算符)
  • 某些需要高效访问私有成员的函数
  • 紧密协作的类之间

6.4 this指针

// 示例6.9:this指针
#include <iostream>
#include <string>
using namespace std;

class Person {
private:
    string name;
    int age;

public:
    // 构造函数中使用this区分参数和成员变量
    Person(string name, int age) {
        this->name = name;  // this->name是成员变量,name是参数
        this->age = age;
    }

    // 返回对象自身的引用,支持链式调用
    Person& setName(string name) {
        this->name = name;
        return *this;  // 返回当前对象的引用
    }

    Person& setAge(int age) {
        this->age = age;
        return *this;  // 返回当前对象的引用
    }

    // 链式调用
    Person& birthday() {
        age++;
        return *this;
    }

    void display() const {
        cout << "姓名: " << name << ", 年龄: " << age << endl;
    }

    // 比较两个对象
    bool isSamePerson(const Person& other) const {
        // this指针指向调用该成员函数的对象
        return (this == &other);  // 比较地址
    }

    // 返回当前对象的指针
    Person* getThis() {
        return this;
    }

    // 静态成员函数没有this指针
    static void test() {
        // cout << name << endl;  // 错误!不能访问非静态成员
        // this->name = "test";   // 错误!静态函数没有this指针
    }
};

int main() {
    cout << "=== this指针演示 ===" << endl;

    Person p1("张三", 20);
    Person p2("李四", 25);

    p1.display();
    p2.display();

    // 链式调用
    cout << "\n链式调用:" << endl;
    p1.setName("张三丰").setAge(100).birthday();
    p1.display();

    // 比较对象
    cout << "\n比较对象:" << endl;
    cout << "p1和p2是同一个对象吗?" 
         << (p1.isSamePerson(p2) ? "是" : "否") << endl;

    Person& ref = p1;
    cout << "p1和ref是同一个对象吗?" 
         << (p1.isSamePerson(ref) ? "是" : "否") << endl;

    // 获取this指针
    cout << "\n获取this指针:" << endl;
    Person* p1Ptr = p1.getThis();
    cout << "p1的地址: " << &p1 << endl;
    cout << "p1.getThis(): " << p1Ptr << endl;

    // this指针在运算符重载中的应用(后面会详细讲)

    return 0;
}

this指针的重要特点

  1. 隐含参数:每个非静态成员函数都有一个隐含的this指针参数
  2. 指向当前对象:指向调用该成员函数的对象
  3. 不能修改:this是常量指针(如Person* const this
  4. 不能用于静态函数:静态函数没有this指针

6.5 对象数组与对象指针

// 示例6.10:对象数组与对象指针
#include <iostream>
#include <vector>
using namespace std;

class Student {
private:
    string name;
    int score;

public:
    // 默认构造函数(必须有,用于创建数组)
    Student() : name("未命名"), score(0) {
        cout << "调用默认构造函数" << endl;
    }

    Student(string n, int s) : name(n), score(s) {
        cout << "调用带参数构造函数" << endl;
    }

    // 拷贝构造函数
    Student(const Student& other) : name(other.name), score(other.score) {
        cout << "调用拷贝构造函数" << endl;
    }

    void display() const {
        cout << "学生: " << name << ", 成绩: " << score << endl;
    }

    void setName(string n) { name = n; }
    void setScore(int s) { score = s; }
};

int main() {
    cout << "=== 对象数组演示 ===" << endl;

    // 1. 对象数组
    // 调用默认构造函数3次
    Student students1[3];

    cout << "\nstudents1数组:" << endl;
    for (int i = 0; i < 3; i++) {
        students1[i].display();
    }

    // 2. 使用初始化列表
    Student students2[3] = {
        Student("张三", 85),    // 调用带参数构造函数
        Student("李四", 92),    // 调用带参数构造函数
        Student()               // 调用默认构造函数
    };

    cout << "\nstudents2数组:" << endl;
    for (int i = 0; i < 3; i++) {
        students2[i].display();
    }

    // 3. 动态对象数组
    cout << "\n动态对象数组:" << endl;
    int n = 3;
    Student* dynamicStudents = new Student[n];  // 调用默认构造函数n次

    // 初始化动态数组
    for (int i = 0; i < n; i++) {
        dynamicStudents[i].setName("学生" + to_string(i+1));
        dynamicStudents[i].setScore(70 + i * 5);
    }

    for (int i = 0; i < n; i++) {
        dynamicStudents[i].display();
    }

    delete[] dynamicStudents;  // 调用每个对象的析构函数

    // 4. 对象指针数组
    cout << "\n对象指针数组:" << endl;
    Student* ptrArray[3];

    // 动态创建对象
    ptrArray[0] = new Student("王五", 88);
    ptrArray[1] = new Student("赵六", 95);
    ptrArray[2] = new Student("孙七", 76);

    for (int i = 0; i < 3; i++) {
        ptrArray[i]->display();
    }

    // 释放内存
    for (int i = 0; i < 3; i++) {
        delete ptrArray[i];
    }

    // 5. vector存储对象
    cout << "\nvector存储对象:" << endl;
    vector<Student> studentVector;

    // 添加对象(会调用拷贝构造函数)
    studentVector.push_back(Student("周八", 82));
    studentVector.push_back(Student("吴九", 91));

    cout << "vector中的学生:" << endl;
    for (const auto& stu : studentVector) {
        stu.display();
    }

    // 6. 指向对象的指针
    cout << "\n指向对象的指针:" << endl;
    Student stu("郑十", 79);
    Student* pStu = &stu;

    // 通过指针访问成员
    pStu->display();        // 等价于 (*pStu).display()
    (*pStu).display();      // 等价于 pStu->display()

    // 修改对象
    pStu->setScore(85);
    stu.display();

    return 0;
}

综合例题与解析

// 例题1:实现一个简单的字符串类
#include <iostream>
#include <cstring>
using namespace std;

class MyString {
private:
    char* data;
    int length;

    // 私有辅助函数
    void copyString(const char* str) {
        if (str == nullptr) {
            data = new char[1];
            data[0] = '\0';
            length = 0;
        } else {
            length = strlen(str);
            data = new char[length + 1];
            strcpy(data, str);
        }
    }

public:
    // 构造函数
    MyString() {
        copyString("");
    }

    MyString(const char* str) {
        copyString(str);
    }

    // 拷贝构造函数
    MyString(const MyString& other) {
        copyString(other.data);
    }

    // 析构函数
    ~MyString() {
        delete[] data;
    }

    // 拷贝赋值运算符
    MyString& operator=(const MyString& other) {
        if (this != &other) {  // 防止自我赋值
            delete[] data;      // 释放原有内存
            copyString(other.data);
        }
        return *this;
    }

    // 获取长度
    int getLength() const {
        return length;
    }

    // 获取C风格字符串
    const char* c_str() const {
        return data;
    }

    // 拼接字符串
    MyString concat(const MyString& other) const {
        char* newStr = new char[length + other.length + 1];
        strcpy(newStr, data);
        strcat(newStr, other.data);
        MyString result(newStr);
        delete[] newStr;
        return result;
    }

    // 重载+运算符(简洁写法)
    MyString operator+(const MyString& other) const {
        return concat(other);
    }

    // 重载+=运算符
    MyString& operator+=(const MyString& other) {
        char* newStr = new char[length + other.length + 1];
        strcpy(newStr, data);
        strcat(newStr, other.data);

        delete[] data;
        data = newStr;
        length += other.length;

        return *this;
    }

    // 重载==运算符
    bool operator==(const MyString& other) const {
        return strcmp(data, other.data) == 0;
    }

    // 重载[]运算符
    char& operator[](int index) {
        if (index < 0 || index >= length) {
            throw out_of_range("索引越界");
        }
        return data[index];
    }

    // const版本的[]运算符
    const char& operator[](int index) const {
        if (index < 0 || index >= length) {
            throw out_of_range("索引越界");
        }
        return data[index];
    }

    // 输出运算符重载(需要友元)
    friend ostream& operator<<(ostream& os, const MyString& str);

    // 输入运算符重载(需要友元)
    friend istream& operator>>(istream& is, MyString& str);
};

// 输出运算符重载实现
ostream& operator<<(ostream& os, const MyString& str) {
    os << str.data;
    return os;
}

// 输入运算符重载实现
istream& operator>>(istream& is, MyString& str) {
    char buffer[1024];
    is >> buffer;
    str = MyString(buffer);
    return is;
}

int main() {
    cout << "=== 自定义字符串类演示 ===" << endl;

    // 测试构造函数
    MyString s1;
    MyString s2("Hello");
    MyString s3 = "World";  // 隐式转换

    cout << "s1: " << s1 << " (长度: " << s1.getLength() << ")" << endl;
    cout << "s2: " << s2 << " (长度: " << s2.getLength() << ")" << endl;
    cout << "s3: " << s3 << " (长度: " << s3.getLength() << ")" << endl;

    // 测试拷贝构造函数
    MyString s4 = s2;  // 调用拷贝构造函数
    cout << "\n拷贝后 s4: " << s4 << endl;

    // 测试赋值运算符
    MyString s5;
    s5 = s3;  // 调用赋值运算符
    cout << "赋值后 s5: " << s5 << endl;

    // 测试自我赋值
    s5 = s5;
    cout << "自我赋值后 s5: " << s5 << endl;

    // 测试+运算符
    MyString s6 = s2 + " " + s3 + "!";
    cout << "\ns2 + \" \" + s3 + \"!\": " << s6 << endl;

    // 测试+=运算符
    s2 += " ";
    s2 += s3;
    s2 += "!";
    cout << "s2 += \" \" += s3 += \"!\": " << s2 << endl;

    // 测试==运算符
    cout << "\n比较字符串:" << endl;
    cout << "s2 == s6: " << (s2 == s6 ? "true" : "false") << endl;
    cout << "s2 == \"Hello World!\": " << (s2 == "Hello World!" ? "true" : "false") << endl;

    // 测试[]运算符
    cout << "\n访问字符:" << endl;
    cout << "s2[0] = " << s2[0] << endl;
    cout << "s2[6] = " << s2[6] << endl;

    // 修改字符
    s2[0] = 'h';
    cout << "修改后 s2: " << s2 << endl;

    // 测试输入
    // cout << "\n请输入一个字符串: ";
    // MyString s7;
    // cin >> s7;
    // cout << "你输入的是: " << s7 << endl;

    return 0;
}

本章小结

重点回顾

  1. 类的基本概念:class vs struct,访问控制,封装
  2. 构造函数与析构函数:各种构造函数,初始化列表,深拷贝
  3. 特殊成员:静态成员,const成员,mutable成员,友元
  4. this指针:指向当前对象,支持链式调用
  5. 对象数组与指针:创建和使用对象数组,对象指针

易错易混淆点

  1. 构造函数初始化顺序:只与成员声明顺序有关
  2. 深拷贝与浅拷贝:需要动态分配内存时必须定义拷贝构造函数
  3. const成员函数:不能修改成员(mutable除外)
  4. 静态成员初始化:必须在类外定义和初始化
  5. this指针:静态函数没有this指针

考研笔试常见题型

  1. 选择题:考察类的基本概念和特性
  2. 程序阅读题:分析类的设计,构造函数调用顺序
  3. 程序填空题:补全类的成员函数实现
  4. 编程题:设计并实现类,如字符串类、日期类等

编程实践建议

  1. 合理设计类:遵循单一职责原则
  2. 使用初始化列表:提高效率,避免重复初始化
  3. 管理资源:如果有动态内存,必须定义拷贝构造函数和赋值运算符
  4. 使用const:尽量将不修改对象的成员函数声明为const
  5. 限制友元:谨慎使用友元,保持封装性

7. 继承与多态(面向对象的核心)

├── 7.1 继承基础
│   ├── 继承概念
│   ├── 派生类定义
│   ├── 访问控制
│   │   ├── 公有继承
│   │   ├── 保护继承
│   │   └── 私有继承
│   └── 继承中的构造与析构
├── 7.2 继承的扩展
│   ├── 多继承
│   ├── 虚继承
│   ├── 菱形继承问题
│   └── 继承与组合对比
├── 7.3 多态
│   ├── 虚函数
│   ├── 纯虚函数与抽象类
│   ├── 虚函数表(vtable)
│   ├── 重写(override)与重载
│   └── 虚析构函数
├── 7.4 运行时类型识别(RTTI)
│   ├── typeid运算符
│   └── dynamic_cast
└── 7.5 访问控制与继承
    ├── using改变访问权限
    └── final与override(C++11)

下面先完善你的目录(按学习/面试高频点补齐),然后给一份可直接用来背诵与自测的复习文档(标准 Markdown)。


继承与多态复习大纲(完善版)

7.1 继承基础

  • 7.1.1 继承概念:is-a / LSP(里氏替换)
  • 7.1.2 派生类定义与语法:class D : public B
  • 7.1.3 访问控制与继承方式
  • 7.1.3.1 成员访问控制:public/protected/private
  • 7.1.3.2 继承方式:公有/保护/私有继承对基类成员可见性的影响
  • 7.1.3.3 protected 的工程含义与风险
  • 7.1.4 名字查找与隐藏(Name Hiding)
  • 7.1.4.1 派生类同名函数隐藏基类重载集合
  • 7.1.4.2 using Base::foo; 引入基类重载
  • 7.1.5 继承中的构造与析构
  • 7.1.5.1 构造顺序:Base→成员→Derived
  • 7.1.5.2 析构顺序:Derived→成员→Base
  • 7.1.5.3 初始化列表与基类构造选择
  • 7.1.5.4 禁止/默认特殊成员:=delete/=default

7.2 继承的扩展

  • 7.2.1 多继承:接口隔离 vs 实现复用
  • 7.2.2 歧义与作用域限定:A::f, B::f
  • 7.2.3 虚继承(virtual inheritance)
  • 7.2.4 菱形继承问题与虚基类初始化规则
  • 7.2.5 继承与组合对比(组合优先)
  • 7.2.5.1 “能组合就组合”的判断准则
  • 7.2.5.2 依赖倒置/接口类(抽象基类)与组合

7.3 多态(运行时多态)

  • 7.3.1 多态的条件:基类指针/引用 + 虚函数
  • 7.3.2 虚函数(virtual)与动态绑定
  • 7.3.3 纯虚函数与抽象类
  • 7.3.4 override / final(C++11)
  • 7.3.5 重写 vs 重载 vs 隐藏(必考三分法)
  • 7.3.6 虚析构函数(virtual destructor)
  • 7.3.6.1 为什么必须:Base* p = new Derived; delete p;
  • 7.3.6.2 纯虚析构也要有定义
  • 7.3.7 对象切片(slicing)与多态拷贝(clone)
  • 7.3.8 默认参数与虚函数(默认参数静态绑定的坑)

7.4 运行时类型识别(RTTI)

  • 7.4.1 typeidstd::type_info
  • 7.4.2 dynamic_cast(安全向下转型)
  • 7.4.2.1 指针失败返回 nullptr
  • 7.4.2.2 引用失败抛异常
  • 7.4.2.3 多继承下的指针调整
  • 7.4.3 RTTI 的使用边界:能用虚函数就别用 RTTI 분支

7.5 工程实践与设计建议(总结章)

  • 7.5.1 Rule of 0/3/5 与多态类资源管理
  • 7.5.2 多态对象的所有权:unique_ptr<Base>
  • 7.5.3 接口类(纯抽象类)写法建议
  • 7.5.4 禁止误用:基类析构 protected 的场景
  • 7.5.5 单元测试建议:利用多态注入 mock(了解即可)

继承与多态复习文档(Markdown)

1. 继承基础速记

1.1 is-a 与 LSP

  • 公有继承表示 is-aDerived 能在需要 Base 的地方使用。
  • LSP(里氏替换):子类替换父类不能破坏父类对外承诺。

1.2 继承方式对可见性影响(核心表)

假设基类成员在基类中是:

基类成员public 继承后protected 继承后private 继承后
publicpublicprotectedprivate
protectedprotectedprotectedprivate
private不可直接访问(始终不可)不可直接访问不可直接访问

重点:基类的 private 成员无论如何派生类都不能直接访问

1.3 构造/析构顺序

  • 构造:Base → 成员 → Derived
  • 析构:Derived → 成员 → Base

构造时基类怎么构造?

  • 必须在初始化列表里指定:Derived() : Base(args...) {}
    否则走 Base()(如果存在)。

1.4 名字隐藏(Name Hiding)

派生类声明同名函数,会隐藏基类同名函数的所有重载

struct Base { void f(int); void f(double); };
struct Derived : Base { void f(int); }; // Base::f(double) 被隐藏

修复方式:

struct Derived : Base {
  using Base::f; // 引入基类重载集合
  void f(int);
};

2. 多态速记

2.1 多态的必要条件

  • 基类指针/引用 指向派生类对象
  • 通过 virtual 函数 调用
Base* p = new Derived;
p->virt(); // 动态绑定,调用 Derived::virt

2.2 override / final

  • override:要求“确实重写了基类虚函数”,写错签名会编译报错。
  • final:禁止继续被重写/继承。
struct Base { virtual void f() = 0; virtual ~Base() = default; };
struct D final : Base { void f() override {} };

2.3 重写 vs 重载 vs 隐藏(必考)

  • 重写 override:发生在继承层次 + 虚函数 + 签名一致
  • 重载 overload:同一作用域,同名不同参
  • 隐藏 hiding:派生类同名声明导致基类同名集合不可见(与 virtual 无关)

3. 虚析构(最重要的工程规则之一)

3.1 什么时候必须虚析构?

只要你可能这样写:

Base* p = new Derived;
delete p;

那么 Base 必须:

virtual ~Base() = default;

否则 delete p 只会调用 ~Base(),派生类析构不执行 → 资源泄漏/未定义行为风险。

3.2 纯虚析构也要有定义

struct Base {
  virtual ~Base() = 0;
};
Base::~Base() = default; // 仍然必须提供定义

4. 对象切片与多态拷贝

4.1 对象切片(slicing)

Base b = Derived{}; // Derived 的那部分被切掉

4.2 多态拷贝(clone 模式)

基类提供:

virtual std::unique_ptr<Base> clone() const = 0;

派生类实现:

std::unique_ptr<Base> clone() const override { return std::make_unique<Derived>(*this); }

5. vtable(虚函数表)你要会说到什么程度

  • 有虚函数的类通常会有 vptr(虚表指针)
  • 动态绑定通过 vtable 查到实际函数地址
  • 这解释了为什么 virtual 析构 能在 delete basePtr 时正确找到派生析构

面试时:说“原理大概如此”,别强行绑定具体编译器实现细节。


6. RTTI:typeid 与 dynamic_cast

6.1 typeid

if (typeid(*p) == typeid(Derived)) { ... }
  • 需要多态类型(通常基类要有虚函数)才能得到“动态类型”。

6.2 dynamic_cast

if (auto* d = dynamic_cast<Derived*>(p)) {
  d->onlyDerived();
}
  • 指针转换失败返回 nullptr
  • 引用转换失败抛 std::bad_cast

6.3 工程建议

  • 优先用虚函数表达行为多态
  • RTTI 多用于:解析外部数据、插件边界、调试/日志、确实需要类型分支时

7. 多继承 / 虚继承 / 菱形继承要点

7.1 多继承风险点

  • 二义性:同名成员来自多个基类
  • 指针调整:Base1*/Base2* 指针值可能不同(同一对象不同子对象地址)

7.2 菱形继承(Diamond)

   A
  / \
 B   C
  \ /
   D
  • 如果不虚继承:D 里会有两份 A 子对象
  • 虚继承:保证只有一份 A 子对象,但构造规则更复杂(最派生类负责初始化虚基类)

8. 工程实践总结(你可以当作“答题模板”)

  1. 多态基类一定 virtual 析构
  2. 资源管理优先 RAII(Rule of 0),少写裸 new/delete
  3. override/final 降低维护成本
  4. 警惕:名字隐藏对象切片默认参数静态绑定
  5. “能组合就组合”,继承用于稳定抽象与多态接口

9. 自测题(快速检查是否掌握)

  1. Base* p = new Derived; delete p; 哪个条件必需?为什么?
  2. Derived 写了 void f(int)Basevoid f(double),为什么 d.f(3.14) 编译不过?怎么修?
  3. 为什么 Base b = Derived{} 会切片?如何避免?
  4. dynamic_cast 用在什么前提上?失败行为是什么?
  5. 虚继承解决了什么问题?代价是什么?

第7章:继承与多态(面向对象核心)

继承和多态是面向对象编程的两个核心概念。继承允许我们定义新的类来继承已有类的属性和行为,多态则允许我们使用统一的接口来处理不同类型的对象。

7.1 继承基础

7.1.1 继承的基本概念

cpp

// 示例7.1:继承的基本概念
#include <iostream>
#include <string>
using namespace std;

// 基类(父类)
class Animal {
protected:  // 保护成员,派生类可以访问,但外部不能访问
    string name;
    int age;

public:
    Animal(const string& n, int a) : name(n), age(a) {
        cout << "Animal构造函数" << endl;
    }

    // 虚析构函数(多态需要)
    virtual ~Animal() {
        cout << "Animal析构函数" << endl;
    }

    void eat() const {
        cout << name << "正在吃东西" << endl;
    }

    void sleep() const {
        cout << name << "正在睡觉" << endl;
    }

    // 虚函数,可以在派生类中重写
    virtual void makeSound() const {
        cout << name << "发出声音" << endl;
    }

    // 非虚函数,派生类可以继承但不能重写(除非重新定义,但不会有多态行为)
    void displayInfo() const {
        cout << "名称: " << name << ", 年龄: " << age << "岁" << endl;
    }
};

// 派生类(子类)Dog,公有继承Animal
class Dog : public Animal {
private:
    string breed;  // 品种

public:
    // 派生类构造函数需要初始化基类部分和新增成员
    Dog(const string& n, int a, const string& b) 
        : Animal(n, a), breed(b) {
        cout << "Dog构造函数" << endl;
    }

    ~Dog() {
        cout << "Dog析构函数" << endl;
    }

    // 重写基类的虚函数
    void makeSound() const override {
        cout << name << "汪汪叫" << endl;
    }

    // 新增函数
    void fetch() const {
        cout << name << "正在捡球" << endl;
    }

    // 使用基类的保护成员
    void setAge(int a) {
        if (a > 0) age = a;
    }

    void displayBreed() const {
        cout << name << "的品种是: " << breed << endl;
    }
};

// 派生类Cat,公有继承Animal
class Cat : public Animal {
private:
    string color;

public:
    Cat(const string& n, int a, const string& c) 
        : Animal(n, a), color(c) {
        cout << "Cat构造函数" << endl;
    }

    ~Cat() {
        cout << "Cat析构函数" << endl;
    }

    // 重写基类的虚函数
    void makeSound() const override {
        cout << name << "喵喵叫" << endl;
    }

    // 新增函数
    void climb() const {
        cout << name << "正在爬树" << endl;
    }

    void displayColor() const {
        cout << name << "的颜色是: " << color << endl;
    }
};

int main() {
    cout << "=== 继承基本概念演示 ===" << endl;

    // 创建基类对象
    cout << "\n1. 创建基类对象:" << endl;
    Animal animal("普通动物", 2);
    animal.eat();
    animal.makeSound();

    // 创建派生类对象
    cout << "\n2. 创建派生类对象:" << endl;
    Dog dog("旺财", 3, "金毛");
    dog.eat();        // 继承自Animal
    dog.makeSound();  // 调用Dog重写的版本
    dog.fetch();      // Dog自己的函数

    Cat cat("咪咪", 2, "白色");
    cat.sleep();      // 继承自Animal
    cat.makeSound();  // 调用Cat重写的版本
    cat.climb();      // Cat自己的函数

    // 通过基类指针调用派生类对象
    cout << "\n3. 通过基类指针调用:" << endl;
    Animal* ptr = &dog;
    ptr->makeSound();  // 多态:调用Dog的makeSound

    ptr = &cat;
    ptr->makeSound();  // 多态:调用Cat的makeSound

    // 注意:不能通过基类指针调用派生类特有的函数
    // ptr->fetch();  // 错误!Animal没有fetch函数

    return 0;
    // 对象销毁时,析构函数调用顺序:先派生类,后基类
}

7.1.2 不同的继承方式

C++有三种继承方式:public、protected和private。

cpp

// 示例7.2:不同的继承方式
#include <iostream>
using namespace std;

class Base {
public:
    int publicVar;
protected:
    int protectedVar;
private:
    int privateVar;

public:
    Base() : publicVar(1), protectedVar(2), privateVar(3) {}
    
    void publicFunc() {
        cout << "Base::publicFunc()" << endl;
    }
    
    void accessMembers() {
        cout << "Base内部访问: ";
        cout << "publicVar=" << publicVar << ", ";
        cout << "protectedVar=" << protectedVar << ", ";
        cout << "privateVar=" << privateVar << endl;
    }
};

// 公有继承
class PublicDerived : public Base {
public:
    void accessBaseMembers() {
        cout << "PublicDerived访问Base成员: ";
        cout << "publicVar=" << publicVar << ", ";        // OK
        cout << "protectedVar=" << protectedVar << ", ";  // OK
        // cout << "privateVar=" << privateVar << endl;   // 错误!不能访问private
    }
};

// 保护继承
class ProtectedDerived : protected Base {
public:
    void accessBaseMembers() {
        cout << "ProtectedDerived访问Base成员: ";
        cout << "publicVar=" << publicVar << ", ";        // OK,但现在是protected
        cout << "protectedVar=" << protectedVar << endl;  // OK
        // privateVar不可访问
    }
    
    // 重新暴露publicFunc为public
    using Base::publicFunc;
};

// 私有继承
class PrivateDerived : private Base {
public:
    void accessBaseMembers() {
        cout << "PrivateDerived访问Base成员: ";
        cout << "publicVar=" << publicVar << ", ";        // OK,但现在是private
        cout << "protectedVar=" << protectedVar << endl;  // OK,但现在是private
        // privateVar不可访问
    }
    
    // 重新暴露publicFunc为public
    using Base::publicFunc;
};

int main() {
    cout << "=== 不同继承方式演示 ===" << endl;
    
    Base base;
    cout << "\n1. Base对象直接访问:" << endl;
    cout << "base.publicVar = " << base.publicVar << endl;  // OK
    base.publicFunc();  // OK
    // cout << base.protectedVar << endl;  // 错误!protected不能外部访问
    // cout << base.privateVar << endl;    // 错误!private不能外部访问
    
    cout << "\n2. 公有继承:" << endl;
    PublicDerived pubDerived;
    cout << "pubDerived.publicVar = " << pubDerived.publicVar << endl;  // OK,仍然是public
    pubDerived.publicFunc();  // OK,仍然是public
    pubDerived.accessBaseMembers();
    
    cout << "\n3. 保护继承:" << endl;
    ProtectedDerived protDerived;
    // cout << protDerived.publicVar << endl;  // 错误!现在是protected
    protDerived.publicFunc();  // 通过using声明重新暴露,可以访问
    protDerived.accessBaseMembers();
    
    cout << "\n4. 私有继承:" << endl;
    PrivateDerived privDerived;
    // cout << privDerived.publicVar << endl;  // 错误!现在是private
    privDerived.publicFunc();  // 通过using声明重新暴露,可以访问
    privDerived.accessBaseMembers();
    
    // 继承方式总结:
    // 公有继承:基类public->派生类public,protected->protected
    // 保护继承:基类public和protected->派生类protected
    // 私有继承:基类public和protected->派生类private
    
    return 0;
}

7.1.3 继承中的构造和析构函数

cpp

// 示例7.3:继承中的构造和析构函数
#include <iostream>
using namespace std;

class Base {
private:
    int baseValue;
    
public:
    // 默认构造函数
    Base() : baseValue(0) {
        cout << "Base默认构造函数,baseValue = " << baseValue << endl;
    }
    
    // 带参数构造函数
    Base(int val) : baseValue(val) {
        cout << "Base带参数构造函数,baseValue = " << baseValue << endl;
    }
    
    // 拷贝构造函数
    Base(const Base& other) : baseValue(other.baseValue) {
        cout << "Base拷贝构造函数,baseValue = " << baseValue << endl;
    }
    
    // 析构函数
    virtual ~Base() {
        cout << "Base析构函数,baseValue = " << baseValue << endl;
    }
    
    int getValue() const { return baseValue; }
    void setValue(int val) { baseValue = val; }
};

class Derived : public Base {
private:
    int derivedValue;
    
public:
    // 默认构造函数
    Derived() : Base(), derivedValue(0) {
        cout << "Derived默认构造函数,derivedValue = " << derivedValue << endl;
    }
    
    // 带参数构造函数
    Derived(int baseVal, int derivedVal) : Base(baseVal), derivedValue(derivedVal) {
        cout << "Derived带参数构造函数,derivedValue = " << derivedValue << endl;
    }
    
    // 拷贝构造函数
    Derived(const Derived& other) : Base(other), derivedValue(other.derivedValue) {
        cout << "Derived拷贝构造函数,derivedValue = " << derivedValue << endl;
    }
    
    // 析构函数
    ~Derived() {
        cout << "Derived析构函数,derivedValue = " << derivedValue << endl;
    }
    
    int getDerivedValue() const { return derivedValue; }
};

int main() {
    cout << "=== 继承中的构造和析构函数 ===" << endl;
    
    cout << "\n1. 创建Derived默认对象:" << endl;
    Derived d1;
    
    cout << "\n2. 创建Derived带参数对象:" << endl;
    Derived d2(10, 20);
    
    cout << "\n3. 创建Derived拷贝对象:" << endl;
    Derived d3 = d2;
    
    cout << "\n4. 动态创建和销毁对象:" << endl;
    Derived* ptr = new Derived(100, 200);
    delete ptr;  // 注意析构顺序
    
    cout << "\n5. 对象数组:" << endl;
    Derived arr[2];  // 调用默认构造函数
    
    cout << "\n6. 作用域结束,自动销毁:" << endl;
    // 注意:析构顺序与构造顺序相反
    return 0;
}

7.2 继承的扩展

7.2.1 多继承

cpp

// 示例7.4:多继承
#include <iostream>
#include <string>
using namespace std;

// 基类1
class Printer {
public:
    void print(const string& text) const {
        cout << "打印: " << text << endl;
    }
};

// 基类2
class Scanner {
public:
    void scan() const {
        cout << "正在扫描..." << endl;
    }
};

// 基类3
class Fax {
public:
    void sendFax(const string& document) const {
        cout << "发送传真: " << document << endl;
    }
    
    void receiveFax() const {
        cout << "接收传真..." << endl;
    }
};

// 多继承:同时继承Printer、Scanner和Fax
class MultifunctionMachine : public Printer, public Scanner, public Fax {
public:
    void copy(const string& document) {
        cout << "开始复印..." << endl;
        scan();           // 调用Scanner的scan
        print(document);  // 调用Printer的print
        cout << "复印完成" << endl;
    }
    
    void multifunctionTask(const string& doc) {
        cout << "执行多功能任务:" << endl;
        scan();
        print(doc);
        sendFax(doc);
    }
};

// 多继承中的问题:菱形继承(钻石问题)
class A {
public:
    int data;
    A() : data(0) { cout << "A构造函数" << endl; }
    ~A() { cout << "A析构函数" << endl; }
};

class B : public A {
public:
    B() { cout << "B构造函数" << endl; }
    ~B() { cout << "B析构函数" << endl; }
};

class C : public A {
public:
    C() { cout << "C构造函数" << endl; }
    ~C() { cout << "C析构函数" << endl; }
};

class D : public B, public C {
public:
    D() { 
        cout << "D构造函数" << endl;
        // 问题:data来自B还是C?实际上有两份data
        B::data = 10;  // 需要指定作用域
        C::data = 20;
    }
    ~D() { cout << "D析构函数" << endl; }
    
    void showData() {
        cout << "B::data = " << B::data << endl;
        cout << "C::data = " << C::data << endl;
    }
};

// 使用虚继承解决菱形继承问题
class VirtualA {
public:
    int data;
    VirtualA() : data(0) { cout << "VirtualA构造函数" << endl; }
    ~VirtualA() { cout << "VirtualA析构函数" << endl; }
};

class VirtualB : virtual public VirtualA {
public:
    VirtualB() { cout << "VirtualB构造函数" << endl; }
    ~VirtualB() { cout << "VirtualB析构函数" << endl; }
};

class VirtualC : virtual public VirtualA {
public:
    VirtualC() { cout << "VirtualC构造函数" << endl; }
    ~VirtualC() { cout << "VirtualC析构函数" << endl; }
};

class VirtualD : public VirtualB, public VirtualC {
public:
    VirtualD() { 
        cout << "VirtualD构造函数" << endl;
        data = 100;  // 现在只有一份data,不需要指定作用域
    }
    ~VirtualD() { cout << "VirtualD析构函数" << endl; }
    
    void showData() {
        cout << "data = " << data << endl;
        // 也可以通过虚基类访问
        cout << "VirtualA::data = " << VirtualA::data << endl;
        cout << "VirtualB::data = " << VirtualB::data << endl;
        cout << "VirtualC::data = " << VirtualC::data << endl;
    }
};

int main() {
    cout << "=== 多继承演示 ===" << endl;
    
    cout << "\n1. 多功能机:" << endl;
    MultifunctionMachine mfm;
    mfm.print("测试文档");
    mfm.scan();
    mfm.sendFax("重要文件");
    mfm.copy("身份证复印件");
    mfm.multifunctionTask("报告");
    
    cout << "\n2. 菱形继承问题:" << endl;
    D d;
    d.showData();
    cout << "sizeof(D) = " << sizeof(d) << endl;
    
    cout << "\n3. 虚继承解决菱形继承:" << endl;
    VirtualD vd;
    vd.showData();
    cout << "sizeof(VirtualD) = " << sizeof(vd) << endl;
    
    return 0;
}

7.2.2 继承与组合

cpp

// 示例7.5:继承与组合
#include <iostream>
#include <string>
#include <vector>
using namespace std;

// 继承:"is-a"关系
class Engine {
private:
    int horsepower;
    
public:
    Engine(int hp) : horsepower(hp) {
        cout << "Engine构造函数: " << horsepower << "马力" << endl;
    }
    
    void start() const {
        cout << "引擎启动,马力: " << horsepower << endl;
    }
    
    void stop() const {
        cout << "引擎停止" << endl;
    }
};

class Wheel {
private:
    int size;  // 英寸
    
public:
    Wheel(int s) : size(s) {
        cout << "Wheel构造函数: " << size << "寸" << endl;
    }
    
    void rotate() const {
        cout << size << "寸轮子旋转" << endl;
    }
};

// 组合:"has-a"关系
class Car {
private:
    string brand;
    Engine engine;      // 组合:Car有一个Engine
    Wheel wheels[4];    // 组合:Car有四个Wheel
    
public:
    Car(const string& b, int engineHP, int wheelSize) 
        : brand(b), engine(engineHP), wheels{Wheel(wheelSize), Wheel(wheelSize), 
                                           Wheel(wheelSize), Wheel(wheelSize)} {
        cout << "Car构造函数: " << brand << endl;
    }
    
    void drive() const {
        cout << brand << "启动:" << endl;
        engine.start();
        for (int i = 0; i < 4; i++) {
            wheels[i].rotate();
        }
        cout << brand << "行驶中..." << endl;
    }
};

// 继承:"is-a"关系
class ElectricEngine : public Engine {
private:
    int batteryCapacity;  // 电池容量
    
public:
    ElectricEngine(int hp, int capacity) 
        : Engine(hp), batteryCapacity(capacity) {
        cout << "ElectricEngine构造函数,电池容量: " << batteryCapacity << "kWh" << endl;
    }
    
    void charge() const {
        cout << "充电中,电池容量: " << batteryCapacity << "kWh" << endl;
    }
    
    // 重写基类函数
    void start() const {
        cout << "电动引擎启动,安静无声" << endl;
    }
};

// 继承与组合结合
class ElectricCar : public Car {
private:
    ElectricEngine electricEngine;  // 组合:ElectricCar有一个ElectricEngine
    int range;  // 续航里程
    
public:
    ElectricCar(const string& b, int engineHP, int wheelSize, int batteryCap, int r) 
        : Car(b, engineHP, wheelSize), 
          electricEngine(engineHP, batteryCap), 
          range(r) {
        cout << "ElectricCar构造函数,续航里程: " << range << "公里" << endl;
    }
    
    void driveElectric() const {
        cout << "电动车模式:" << endl;
        electricEngine.start();
        electricEngine.charge();
        cout << "续航里程: " << range << "公里" << endl;
    }
};

// 继承 vs 组合的选择
class Person {
protected:
    string name;
    int age;
    
public:
    Person(const string& n, int a) : name(n), age(a) {}
    
    void introduce() const {
        cout << "我叫" << name << ",今年" << age << "岁" << endl;
    }
};

// 继承:Student "is-a" Person
class Student : public Person {
private:
    string studentId;
    
public:
    Student(const string& n, int a, const string& id) 
        : Person(n, a), studentId(id) {}
    
    void study() const {
        cout << name << "(学号: " << studentId << ")正在学习" << endl;
    }
};

// 组合:University "has-a" Person
class University {
private:
    string name;
    vector<Person*> people;  // 组合:大学有多个人员
    
public:
    University(const string& n) : name(n) {}
    
    void addPerson(Person* person) {
        people.push_back(person);
    }
    
    void showAllPeople() const {
        cout << name << "的所有人员:" << endl;
        for (const auto& person : people) {
            person->introduce();
        }
    }
};

int main() {
    cout << "=== 继承与组合演示 ===" << endl;
    
    cout << "\n1. 组合示例(Car has Engine and Wheels):" << endl;
    Car car("Toyota", 150, 18);
    car.drive();
    
    cout << "\n2. 继承示例(ElectricCar is a Car):" << endl;
    ElectricCar tesla("Tesla", 300, 20, 100, 500);
    tesla.drive();           // 继承自Car
    tesla.driveElectric();   // ElectricCar自己的方法
    
    cout << "\n3. 继承与组合比较:" << endl;
    Student student("张三", 20, "2023001");
    student.introduce();     // 继承自Person
    student.study();         // Student自己的方法
    
    University university("清华大学");
    university.addPerson(&student);
    
    Person teacher("李老师", 35);
    university.addPerson(&teacher);
    
    university.showAllPeople();
    
    return 0;
}

7.3 多态

7.3.1 虚函数与纯虚函数

cpp

// 示例7.6:虚函数与纯虚函数
#include <iostream>
#include <vector>
#include <memory>
using namespace std;

// 抽象基类:包含纯虚函数
class Shape {
protected:
    string name;
    string color;
    
public:
    Shape(const string& n, const string& c) : name(n), color(c) {}
    
    virtual ~Shape() {
        cout << "Shape析构函数: " << name << endl;
    }
    
    // 纯虚函数:没有实现,派生类必须实现
    virtual double area() const = 0;
    
    // 纯虚函数:派生类必须实现
    virtual double perimeter() const = 0;
    
    // 虚函数:有默认实现,派生类可以重写
    virtual void draw() const {
        cout << "绘制" << color << "的" << name << endl;
    }
    
    // 非虚函数:派生类可以继承但不能重写(除非重新定义,但不会有多态)
    void displayInfo() const {
        cout << name << " (" << color << "): ";
        cout << "面积 = " << area() << ", 周长 = " << perimeter() << endl;
    }
    
    // 虚函数:有默认实现
    virtual void scale(double factor) {
        cout << name << "缩放 " << factor << " 倍" << endl;
    }
};

// 派生类1:必须实现所有纯虚函数
class Circle : public Shape {
private:
    double radius;
    
public:
    Circle(double r, const string& c = "红色") 
        : Shape("圆形", c), radius(r) {}
    
    // 实现纯虚函数
    double area() const override {
        return 3.14159 * radius * radius;
    }
    
    double perimeter() const override {
        return 2 * 3.14159 * radius;
    }
    
    // 重写虚函数
    void draw() const override {
        cout << "绘制" << color << "的圆形,半径" << radius << endl;
    }
    
    void scale(double factor) override {
        radius *= factor;
        cout << "圆形半径变为 " << radius << endl;
    }
    
    // 新增函数
    double getRadius() const { return radius; }
};

// 派生类2
class Rectangle : public Shape {
private:
    double width;
    double height;
    
public:
    Rectangle(double w, double h, const string& c = "蓝色") 
        : Shape("矩形", c), width(w), height(h) {}
    
    // 实现纯虚函数
    double area() const override {
        return width * height;
    }
    
    double perimeter() const override {
        return 2 * (width + height);
    }
    
    // 重写虚函数
    void draw() const override {
        cout << "绘制" << color << "的矩形,"
             << width << "×" << height << endl;
    }
    
    void scale(double factor) override {
        width *= factor;
        height *= factor;
        cout << "矩形尺寸变为 " << width << "×" << height << endl;
    }
    
    // 新增函数
    bool isSquare() const {
        return width == height;
    }
};

// 派生类3:继承自Rectangle
class Square : public Rectangle {
public:
    Square(double side, const string& c = "绿色") 
        : Rectangle(side, side, "正方形") {  // 注意:调用基类构造函数
        name = "正方形";  // 可以修改基类的保护成员
        color = c;
    }
    
    // 重写虚函数
    void draw() const override {
        cout << "绘制" << color << "的正方形,边长" << getWidth() << endl;
    }
    
    // 获取宽度(矩形有宽度和高度,正方形相同)
    double getWidth() const { 
        // 注意:width是Rectangle的private成员,不能直接访问
        // 需要通过Rectangle的公共接口访问
        // 这里简化处理,实际应该提供getter
        return area() / height;  // 通过面积和高度计算
    }
};

// 工厂函数:返回基类指针
Shape* createShape(const string& type) {
    if (type == "circle") {
        return new Circle(10.0);
    } else if (type == "rectangle") {
        return new Rectangle(5.0, 8.0);
    } else if (type == "square") {
        return new Square(6.0);
    }
    return nullptr;
}

int main() {
    cout << "=== 虚函数与纯虚函数演示 ===" << endl;
    
    // 不能创建抽象类的对象
    // Shape shape;  // 错误!Shape是抽象类
    
    cout << "\n1. 创建具体形状对象:" << endl;
    Circle circle(5.0, "红色");
    Rectangle rectangle(4.0, 6.0, "蓝色");
    Square square(5.0, "绿色");
    
    circle.displayInfo();
    rectangle.displayInfo();
    square.displayInfo();
    
    cout << "\n2. 通过基类指针调用多态函数:" << endl;
    Shape* shapes[3];
    shapes[0] = &circle;
    shapes[1] = &rectangle;
    shapes[2] = &square;
    
    for (int i = 0; i < 3; i++) {
        shapes[i]->draw();          // 多态调用
        shapes[i]->displayInfo();   // 非多态调用
        shapes[i]->scale(1.5);      // 多态调用
        cout << endl;
    }
    
    cout << "\n3. 使用动态内存和智能指针:" << endl;
    vector<unique_ptr<Shape>> shapeList;
    shapeList.push_back(make_unique<Circle>(3.0));
    shapeList.push_back(make_unique<Rectangle>(2.0, 4.0));
    shapeList.push_back(make_unique<Square>(5.0));
    
    for (const auto& shape : shapeList) {
        shape->draw();
        shape->displayInfo();
        cout << endl;
    }
    
    cout << "\n4. 工厂函数创建对象:" << endl;
    Shape* s1 = createShape("circle");
    Shape* s2 = createShape("square");
    
    if (s1) {
        s1->displayInfo();
        delete s1;
    }
    
    if (s2) {
        s2->displayInfo();
        delete s2;
    }
    
    return 0;
}

7.3.2 虚函数表与动态绑定

cpp

// 示例7.7:虚函数表与动态绑定
#include <iostream>
using namespace std;

class Base {
public:
    virtual void func1() {
        cout << "Base::func1()" << endl;
    }
    
    virtual void func2() {
        cout << "Base::func2()" << endl;
    }
    
    void nonVirtual() {
        cout << "Base::nonVirtual()" << endl;
    }
    
    virtual ~Base() {}
};

class Derived : public Base {
public:
    void func1() override {
        cout << "Derived::func1()" << endl;
    }
    
    // 不重写func2,继承Base的func2
    
    void nonVirtual() {
        cout << "Derived::nonVirtual()" << endl;
    }
    
    virtual void func3() {
        cout << "Derived::func3()" << endl;
    }
};

// 演示动态绑定的工作原理
void demonstrateDynamicBinding() {
    cout << "=== 动态绑定演示 ===" << endl;
    
    Base base;
    Derived derived;
    
    cout << "\n1. 通过对象直接调用:" << endl;
    base.func1();        // 静态绑定:调用Base::func1
    derived.func1();     // 静态绑定:调用Derived::func1
    derived.func2();     // 静态绑定:调用Base::func2(继承)
    derived.nonVirtual(); // 静态绑定:调用Derived::nonVirtual
    
    cout << "\n2. 通过基类指针调用(多态):" << endl;
    Base* ptr = &derived;
    ptr->func1();        // 动态绑定:调用Derived::func1
    ptr->func2();        // 动态绑定:调用Base::func2
    ptr->nonVirtual();   // 静态绑定:调用Base::nonVirtual(非虚函数)
    
    cout << "\n3. 通过基类引用调用:" << endl;
    Base& ref = derived;
    ref.func1();         // 动态绑定:调用Derived::func1
    ref.nonVirtual();    // 静态绑定:调用Base::nonVirtual
    
    cout << "\n4. 切片问题(对象切片):" << endl;
    Base sliced = derived;  // 切片:只复制Base部分
    sliced.func1();         // 静态绑定:调用Base::func1(不是Derived的!)
    sliced.nonVirtual();    // 静态绑定:调用Base::nonVirtual
}

// 演示虚函数表的存在
void demonstrateVTable() {
    cout << "\n=== 虚函数表存在性演示 ===" << endl;
    
    Base base;
    Derived derived;
    
    // 获取对象大小(包含虚函数表指针)
    cout << "sizeof(Base) = " << sizeof(base) << endl;
    cout << "sizeof(Derived) = " << sizeof(derived) << endl;
    
    // 对于32位系统,指针大小是4字节
    // 对于64位系统,指针大小是8字节
    // 虚函数表指针通常是一个指针的大小
    
    cout << "\n对象布局:" << endl;
    cout << "Base对象包含:虚函数表指针" << endl;
    cout << "Derived对象包含:Base部分(虚函数表指针) + Derived新增部分" << endl;
}

int main() {
    demonstrateDynamicBinding();
    demonstrateVTable();
    
    return 0;
}

7.4 运行时类型识别(RTTI)

cpp

// 示例7.8:运行时类型识别(RTTI)
#include <iostream>
#include <typeinfo>
#include <vector>
using namespace std;

class Animal {
public:
    virtual ~Animal() {}
    virtual void speak() const = 0;
};

class Dog : public Animal {
public:
    void speak() const override {
        cout << "汪汪!" << endl;
    }
    
    void fetch() const {
        cout << "捡球" << endl;
    }
};

class Cat : public Animal {
public:
    void speak() const override {
        cout << "喵喵!" << endl;
    }
    
    void climb() const {
        cout << "爬树" << endl;
    }
};

class Bird : public Animal {
public:
    void speak() const override {
        cout << "叽叽喳喳!" << endl;
    }
    
    void fly() const {
        cout << "飞翔" << endl;
    }
};

void demonstrateTypeid() {
    cout << "=== typeid运算符演示 ===" << endl;
    
    Animal* animals[] = {
        new Dog(),
        new Cat(),
        new Bird(),
        new Dog()
    };
    
    for (int i = 0; i < 4; i++) {
        // 使用typeid获取类型信息
        const type_info& ti = typeid(*animals[i]);
        cout << "animal[" << i << "] 的类型是: " << ti.name() << endl;
        
        // 比较类型
        if (ti == typeid(Dog)) {
            cout << "  -> 这是一只狗" << endl;
        } else if (ti == typeid(Cat)) {
            cout << "  -> 这是一只猫" << endl;
        } else if (ti == typeid(Bird)) {
            cout << "  -> 这是一只鸟" << endl;
        }
        
        animals[i]->speak();
        cout << endl;
    }
    
    // 清理内存
    for (int i = 0; i < 4; i++) {
        delete animals[i];
    }
}

void demonstrateDynamicCast() {
    cout << "\n=== dynamic_cast运算符演示 ===" << endl;
    
    vector<Animal*> animals;
    animals.push_back(new Dog());
    animals.push_back(new Cat());
    animals.push_back(new Bird());
    animals.push_back(new Dog());
    animals.push_back(new Cat());
    
    int dogCount = 0, catCount = 0, birdCount = 0;
    
    for (size_t i = 0; i < animals.size(); i++) {
        animals[i]->speak();
        
        // 尝试向下转型
        if (Dog* dog = dynamic_cast<Dog*>(animals[i])) {
            cout << "  转换为Dog成功,调用fetch()" << endl;
            dog->fetch();
            dogCount++;
        } 
        else if (Cat* cat = dynamic_cast<Cat*>(animals[i])) {
            cout << "  转换为Cat成功,调用climb()" << endl;
            cat->climb();
            catCount++;
        } 
        else if (Bird* bird = dynamic_cast<Bird*>(animals[i])) {
            cout << "  转换为Bird成功,调用fly()" << endl;
            bird->fly();
            birdCount++;
        } 
        else {
            cout << "  转换失败" << endl;
        }
        
        cout << endl;
    }
    
    cout << "统计结果:" << endl;
    cout << "狗: " << dogCount << " 只" << endl;
    cout << "猫: " << catCount << " 只" << endl;
    cout << "鸟: " << birdCount << " 只" << endl;
    
    // 清理内存
    for (auto animal : animals) {
        delete animal;
    }
    
    cout << "\n=== dynamic_cast失败情况 ===" << endl;
    
    // 失败的dynamic_cast
    Animal* animal = new Dog();
    
    // 正确转型
    Dog* dog = dynamic_cast<Dog*>(animal);
    if (dog) {
        cout << "Animal* -> Dog* 成功" << endl;
    }
    
    // 错误转型
    Cat* cat = dynamic_cast<Cat*>(animal);
    if (!cat) {
        cout << "Animal* -> Cat* 失败,返回nullptr" << endl;
    }
    
    delete animal;
    
    // 引用转型(失败会抛出异常)
    try {
        Dog d;
        Animal& a = d;
        
        // 成功转型
        Dog& rd = dynamic_cast<Dog&>(a);
        cout << "Animal& -> Dog& 成功" << endl;
        
        // 尝试错误转型(会抛出bad_cast异常)
        // Cat& rc = dynamic_cast<Cat&>(a);  // 抛出异常
    } catch (const bad_cast& e) {
        cout << "引用转型失败: " << e.what() << endl;
    }
}

int main() {
    demonstrateTypeid();
    demonstrateDynamicCast();
    
    return 0;
}

7.5 访问控制与继承(C++11)

cpp

// 示例7.9:C++11中的final与override
#include <iostream>
using namespace std;

// final类:不能被继承
class Base final {  // 添加final关键字
private:
    int value;
    
public:
    Base(int v) : value(v) {}
    
    int getValue() const { return value; }
};

// 错误:不能继承final类
// class Derived : public Base {};

// override和final在成员函数中的使用
class Animal {
public:
    virtual ~Animal() {}
    
    // 虚函数
    virtual void speak() const {
        cout << "动物发出声音" << endl;
    }
    
    // 虚函数,希望派生类重写
    virtual void move() const {
        cout << "动物移动" << endl;
    }
    
    // 虚函数,不希望派生类重写(但可以被重写)
    virtual void eat() const {
        cout << "动物吃东西" << endl;
    }
    
    // 非虚函数
    void sleep() const {
        cout << "动物睡觉" << endl;
    }
};

class Dog : public Animal {
public:
    // 正确:重写基类虚函数
    void speak() const override {
        cout << "狗汪汪叫" << endl;
    }
    
    // 正确:使用override确保重写的是虚函数
    void move() const override {
        cout << "狗跑动" << endl;
    }
    
    // 正确:可以重写,但使用final禁止进一步重写
    virtual void eat() const override final {
        cout << "狗吃骨头" << endl;
    }
    
    // 错误:尝试重写非虚函数
    // void sleep() const override {}  // 错误!基类sleep不是虚函数
    
    // 新增虚函数
    virtual void fetch() {
        cout << "狗捡球" << endl;
    }
};

class SpecialDog : public Dog {
public:
    // 正确:重写Dog的speak
    void speak() const override {
        cout << "特殊狗叫" << endl;
    }
    
    // 错误:不能重写final函数
    // void eat() const override {}  // 错误!Dog::eat是final
    
    // 可以重写fetch
    void fetch() override {
        cout << "特殊狗捡飞盘" << endl;
    }
};

// final成员函数
class Base2 {
public:
    virtual void func() final {  // 声明为final
        cout << "Base2::func()" << endl;
    }
    
    virtual void func2() {
        cout << "Base2::func2()" << endl;
    }
};

class Derived2 : public Base2 {
public:
    // 错误:不能重写final函数
    // void func() override {}
    
    // 正确:可以重写func2
    void func2() override {
        cout << "Derived2::func2()" << endl;
    }
};

// using改变访问权限
class Base3 {
protected:
    void protectedFunc() {
        cout << "Base3::protectedFunc()" << endl;
    }
    
public:
    void publicFunc() {
        cout << "Base3::publicFunc()" << endl;
    }
};

class Derived3 : private Base3 {  // 私有继承
public:
    // 使用using将基类的protected成员提升为public
    using Base3::protectedFunc;
    
    // 使用using将基类的public成员保持为public(否则会变成private)
    using Base3::publicFunc;
    
    void test() {
        protectedFunc();  // 可以访问,因为using声明
        publicFunc();     // 可以访问
    }
};

int main() {
    cout << "=== final与override演示 ===" << endl;
    
    cout << "\n1. override关键字:" << endl;
    Dog dog;
    dog.speak();  // 调用Dog::speak
    dog.move();   // 调用Dog::move
    dog.eat();    // 调用Dog::eat
    dog.sleep();  // 调用Animal::sleep(继承)
    
    cout << "\n2. final类:" << endl;
    Base base(10);
    cout << "base.getValue() = " << base.getValue() << endl;
    
    cout << "\n3. 进一步继承:" << endl;
    SpecialDog specialDog;
    specialDog.speak();  // 调用SpecialDog::speak
    specialDog.fetch();  // 调用SpecialDog::fetch
    specialDog.eat();    // 调用Dog::eat(final,不能重写)
    
    cout << "\n4. using改变访问权限:" << endl;
    Derived3 d3;
    d3.publicFunc();     // 可以访问
    d3.protectedFunc();  // 可以访问(通过using声明)
    d3.test();
    
    return 0;
}

综合例题与解析

cpp

// 例题:实现一个简单的图形编辑器
#include <iostream>
#include <vector>
#include <memory>
#include <cmath>
#include <algorithm>
using namespace std;

// 抽象图形类
class Graphic {
protected:
    int x, y;  // 位置
    string color;
    
public:
    Graphic(int xPos, int yPos, const string& c) 
        : x(xPos), y(yPos), color(c) {}
    
    virtual ~Graphic() {}
    
    // 纯虚函数
    virtual void draw() const = 0;
    virtual double area() const = 0;
    virtual Graphic* clone() const = 0;  // 克隆模式
    
    // 虚函数
    virtual void move(int dx, int dy) {
        x += dx;
        y += dy;
        cout << "图形移动到 (" << x << ", " << y << ")" << endl;
    }
    
    virtual void scale(double factor) {
        cout << "图形缩放 " << factor << " 倍" << endl;
    }
    
    // 非虚函数
    void setColor(const string& c) {
        color = c;
        cout << "颜色改为: " << color << endl;
    }
    
    string getColor() const { return color; }
    int getX() const { return x; }
    int getY() const { return y; }
    
    // 静态成员
    static int getGraphicCount() {
        return graphicCount;
    }
    
protected:
    static int graphicCount;  // 统计创建的图形数量
};

int Graphic::graphicCount = 0;

// 圆形类
class Circle : public Graphic {
private:
    double radius;
    
public:
    Circle(int x, int y, double r, const string& c = "黑色") 
        : Graphic(x, y, c), radius(r) {
        graphicCount++;
    }
    
    Circle(const Circle& other) 
        : Graphic(other.x, other.y, other.color), radius(other.radius) {
        graphicCount++;
    }
    
    ~Circle() {
        graphicCount--;
    }
    
    void draw() const override {
        cout << "在 (" << x << ", " << y << ") 绘制 "
             << color << " 的圆形,半径 " << radius << endl;
    }
    
    double area() const override {
        return 3.14159 * radius * radius;
    }
    
    void scale(double factor) override {
        radius *= factor;
        cout << "圆形半径变为 " << radius << endl;
    }
    
    Graphic* clone() const override {
        return new Circle(*this);
    }
    
    double getRadius() const { return radius; }
};

// 矩形类
class Rectangle : public Graphic {
private:
    double width, height;
    
public:
    Rectangle(int x, int y, double w, double h, const string& c = "黑色") 
        : Graphic(x, y, c), width(w), height(h) {
        graphicCount++;
    }
    
    Rectangle(const Rectangle& other) 
        : Graphic(other.x, other.y, other.color), 
          width(other.width), height(other.height) {
        graphicCount++;
    }
    
    ~Rectangle() {
        graphicCount--;
    }
    
    void draw() const override {
        cout << "在 (" << x << ", " << y << ") 绘制 "
             << color << " 的矩形,尺寸 " 
             << width << "×" << height << endl;
    }
    
    double area() const override {
        return width * height;
    }
    
    void scale(double factor) override {
        width *= factor;
        height *= factor;
        cout << "矩形尺寸变为 " << width << "×" << height << endl;
    }
    
    Graphic* clone() const override {
        return new Rectangle(*this);
    }
    
    bool isSquare() const {
        return abs(width - height) < 0.0001;
    }
};

// 三角形类
class Triangle : public Graphic {
private:
    double base, height;
    
public:
    Triangle(int x, int y, double b, double h, const string& c = "黑色") 
        : Graphic(x, y, c), base(b), height(h) {
        graphicCount++;
    }
    
    Triangle(const Triangle& other) 
        : Graphic(other.x, other.y, other.color), 
          base(other.base), height(other.height) {
        graphicCount++;
    }
    
    ~Triangle() {
        graphicCount--;
    }
    
    void draw() const override {
        cout << "在 (" << x << ", " << y << ") 绘制 "
             << color << " 的三角形,底 " 
             << base << ",高 " << height << endl;
    }
    
    double area() const override {
        return 0.5 * base * height;
    }
    
    void scale(double factor) override {
        base *= factor;
        height *= factor;
        cout << "三角形尺寸变为 底" << base << ",高" << height << endl;
    }
    
    Graphic* clone() const override {
        return new Triangle(*this);
    }
};

// 图形编辑器
class GraphicEditor {
private:
    vector<unique_ptr<Graphic>> graphics;
    
public:
    void addGraphic(unique_ptr<Graphic> graphic) {
        graphics.push_back(move(graphic));
    }
    
    void drawAll() const {
        cout << "\n=== 绘制所有图形 ===" << endl;
        for (const auto& graphic : graphics) {
            graphic->draw();
        }
    }
    
    void scaleAll(double factor) {
        cout << "\n=== 缩放所有图形 ===" << endl;
        for (const auto& graphic : graphics) {
            graphic->scale(factor);
        }
    }
    
    void moveAll(int dx, int dy) {
        cout << "\n=== 移动所有图形 ===" << endl;
        for (const auto& graphic : graphics) {
            graphic->move(dx, dy);
        }
    }
    
    double totalArea() const {
        double total = 0;
        for (const auto& graphic : graphics) {
            total += graphic->area();
        }
        return total;
    }
    
    // 复制所有图形
    GraphicEditor clone() const {
        GraphicEditor editor;
        for (const auto& graphic : graphics) {
            editor.addGraphic(unique_ptr<Graphic>(graphic->clone()));
        }
        return editor;
    }
    
    // 按面积排序
    void sortByArea() {
        sort(graphics.begin(), graphics.end(), 
             [](const unique_ptr<Graphic>& a, const unique_ptr<Graphic>& b) {
                 return a->area() < b->area();
             });
        cout << "\n图形已按面积排序" << endl;
    }
    
    void displayInfo() const {
        cout << "\n=== 图形信息 ===" << endl;
        for (size_t i = 0; i < graphics.size(); i++) {
            cout << "图形 " << i << ": ";
            graphics[i]->draw();
            cout << "  面积: " << graphics[i]->area() << endl;
        }
        cout << "总图形数: " << graphics.size() << endl;
        cout << "总面积: " << totalArea() << endl;
        cout << "全局图形计数: " << Graphic::getGraphicCount() << endl;
    }
};

int main() {
    cout << "=== 图形编辑器演示 ===" << endl;
    
    GraphicEditor editor;
    
    // 添加图形
    editor.addGraphic(make_unique<Circle>(100, 100, 50, "红色"));
    editor.addGraphic(make_unique<Rectangle>(200, 200, 80, 60, "蓝色"));
    editor.addGraphic(make_unique<Triangle>(300, 300, 100, 50, "绿色"));
    editor.addGraphic(make_unique<Circle>(400, 400, 30, "黄色"));
    
    // 操作图形
    editor.drawAll();
    editor.displayInfo();
    
    editor.scaleAll(1.5);
    editor.moveAll(10, 10);
    
    editor.displayInfo();
    
    // 排序
    editor.sortByArea();
    editor.displayInfo();
    
    // 克隆编辑器
    cout << "\n=== 克隆编辑器 ===" << endl;
    GraphicEditor clonedEditor = editor.clone();
    cout << "克隆后的编辑器:" << endl;
    clonedEditor.displayInfo();
    
    // 修改克隆后的编辑器
    clonedEditor.moveAll(-20, -20);
    clonedEditor.scaleAll(0.5);
    cout << "\n修改后的克隆编辑器:" << endl;
    clonedEditor.displayInfo();
    
    cout << "\n原始编辑器:" << endl;
    editor.displayInfo();
    
    return 0;
}

本章小结

重点回顾

  1. 继承的基本概念:公有、保护、私有继承
  2. 构造和析构顺序:先基类后派生类的构造,先派生类后基类的析构
  3. 多继承与虚继承:解决菱形继承问题
  4. 多态的实现:虚函数、纯虚函数、抽象类
  5. 虚函数表与动态绑定:运行时多态的实现机制
  6. RTTI:typeid和dynamic_cast的使用
  7. C++11新特性:final、override、using改变访问权限

易错易混淆点

  1. 访问控制:不同继承方式对基类成员访问权限的影响
  2. 切片问题:派生类对象赋值给基类对象时丢失派生类特有信息
  3. 虚析构函数:基类指针删除派生类对象时必须使用虚析构函数
  4. 纯虚函数与抽象类:含有纯虚函数的类是抽象类,不能实例化
  5. 重载、隐藏与重写
    • 重载:同一作用域,函数名相同但参数不同
    • 隐藏:派生类函数屏蔽基类同名函数
    • 重写(覆盖):派生类重写基类的虚函数

考研笔试常见题型

  1. 选择题:考察继承和多态的基本概念
  2. 程序阅读题:分析继承关系、函数调用、对象构造析构顺序
  3. 程序填空题:补全继承相关的代码
  4. 编程题:设计类的继承体系,实现多态

编程实践建议

  1. 合理设计继承层次:遵循”is-a”关系使用继承,”has-a”关系使用组合
  2. 使用虚析构函数:基类通常应该声明虚析构函数
  3. 使用override关键字:明确表示重写虚函数,避免错误
  4. 使用final关键字:防止不必要的继承或重写
  5. 避免过度使用继承:优先使用组合,继承会带来紧耦合
  6. 理解虚函数开销:虚函数调用有额外开销,不要滥用

8. 运算符重载

├── 8.1 运算符重载基础
│   ├── 可重载运算符
│   ├── 不可重载运算符
│   ├── 成员函数重载
│   └── 友元函数重载
├── 8.2 常用运算符重载
│   ├── 算术运算符重载
│   ├── 关系运算符重载
│   ├── 赋值运算符重载
│   ├── 下标运算符重载([])
│   ├── 函数调用运算符重载(())
│   ├── 自增自减运算符重载
│   ├── 输入输出运算符重载(<<, >>)
│   └── 类型转换运算符重载
└── 8.3 特殊运算符重载
    ├── 拷贝赋值运算符
    ├── 移动赋值运算符(C++11)
    └── new/delete运算符重载

第 8 章:C++ 运算符重载笔记(标准版)

目标:掌握“哪些能/不能重载、成员 vs 非成员/友元怎么选、常见运算符的推荐签名、返回值/返回引用规则、易错点”。


8.1 运算符重载基础

8.1.1 可重载运算符(常见)

  • 算术:+ - * / %
  • 复合赋值:+= -= *= /= %= &= |= ^= <<= >>=
  • 关系:== != < <= > >=
  • 位运算:& | ^ ~ << >>(位移)
  • 逻辑:!&& || 也可重载但一般不推荐)
  • 自增自减:++ --
  • 其他:[] () -> * ,
  • 内存:new delete new[] delete[]
  • 类型转换:operator T()

规律:能重载 ≠ 应该重载,重载要遵循“直觉语义”。


8.1.2 不可重载运算符(必背)

  • .(成员访问)
  • .*(成员指针访问)
  • ::(作用域)
  • ?:(三目)
  • sizeof
  • typeid
  • alignof
  • decltype

并且:

  • 不能改变运算符的优先级、结合性、操作数个数。

8.1.3 成员函数重载(Member)

表达式:

  • a += b 等价于 a.operator+=(b)
  • a[i] 等价于 a.operator[](i)

特点:

  • 左操作数必须是该类对象
  • 可直接访问私有成员
  • 常用于“修改自身”的运算符

必须是成员函数的运算符:

  • operator=
  • operator[]
  • operator()
  • operator->

8.1.4 非成员/友元函数重载(non-member / friend)

表达式:

  • a + b 等价于 operator+(a, b)
  • cout << x 等价于 operator<<(cout, x)

特点:

  • 对称性更好(两侧都能参与隐式转换)
  • << >> 几乎必须是非成员(左操作数是流类型)
  • 访问私有成员可用 friend(或提供 public 接口)

8.2 常用运算符重载

8.2.1 算术运算符(+ - * / %

推荐套路:

  • 复合运算(如 +=)写成员:修改自身并返回引用
  • + 写非成员:按值接收 lhs,复用 +=

常见模板:

T& T::operator+=(const T& rhs);     // 成员,修改自身

T operator+(T lhs, const T& rhs) {  // 非成员,产生新值
    lhs += rhs;
    return lhs;
}

要点:

  • operator+ 不能写成 return lhs + rhs;(会递归调用自己)
  • lhs 按值是刻意设计:得到副本,避免改到原左操作数

8.2.2 关系运算符(== != < <= > >=

模板:

bool operator==(const T& a, const T& b);
bool operator<(const T& a, const T& b); // 若用于排序容器,必须满足严格弱序

要点:

  • 返回 bool(值)
  • 参数通常用 const T&(不拷贝且不修改)

8.2.3 赋值运算符(operator=

标准签名:

T& T::operator=(const T& rhs);

要点:

  • 返回 *this(引用),支持链式:a = b = c;
  • 若类管理资源(如裸指针),要遵守 Rule of 3/5(见 8.3)

8.2.4 下标运算符 operator[](必须成员)

模板(强烈建议提供 const / 非 const 两个版本):

T& operator[](size_t i);
const T& operator[](size_t i) const;

要点:

  • 非 const 返回 T& 允许修改:a[i] = ...
  • const 返回 const T& 保证只读

8.2.5 函数调用运算符 operator()(必须成员)

模板:

R operator()(Args... args) const;

用途:

  • 函数对象(仿函数)、自定义回调、策略类、比较器等

8.2.6 自增自减(++ --

区分前置与后置(必考):

  • 前置:先改再返回自身引用
T& operator++();    // ++a
T& operator--();    // --a
  • 后置:返回旧值(副本),再修改自身
T operator++(int);  // a++
T operator--(int);  // a--

要点:

  • 后置的 int 只是占位用于区分签名
  • 后置返回值(按值)是合理的:语义要求“旧值”

8.2.7 输入输出(<< >>

几乎总是非成员(常为 friend):

std::ostream& operator<<(std::ostream& os, const T& obj);
std::istream& operator>>(std::istream& is, T& obj);

要点:

  • 返回流引用支持链式:cout << a << b;cin >> a >> b;
  • >> 的对象参数必须是 T&(要写入对象)
  • ostream/istream 不可拷贝,因此不能按值返回/按值接收

8.2.8 类型转换运算符 operator T()

模板:

explicit operator bool() const; // 常用且建议 explicit
operator int() const;           // 视需求

要点:

  • 易引发隐式转换歧义,很多转换应加 explicit

8.3 特殊运算符重载

8.3.1 拷贝赋值运算符(Copy Assignment)

签名:

T& operator=(const T& rhs);

当你手动管理资源时要考虑:

  • 深拷贝
  • 自赋值
  • 异常安全(常见 copy-and-swap)

8.3.2 移动赋值运算符(Move Assignment,C++11)

签名:

T& operator=(T&& rhs) noexcept;

目的:

  • “窃取”资源,避免深拷贝
  • 对容器性能影响巨大(扩容、返回值等)

8.3.3 new/delete 运算符重载

常见用途:

  • 内存池、调试统计、对齐需求
  • 需要非常谨慎(容易造成内存管理问题)

模板概念:

void* operator new(std::size_t);
void  operator delete(void*) noexcept;

高频规则总结(背这个)

1) 返回引用(多):修改自身 + 支持链式

通常返回 T&

  • 复合赋值:+= -= *= ...
  • 赋值:=
  • 前置 ++a --a
  • operator[](非 const 版本)
  • 流:ostream& operator<<istream& operator>>

2) 返回值(少):产生新结果 / 返回旧值副本

通常返回 Tbool

  • + - * / %(非复合)
  • 一元:- + ~ !
  • 关系运算:返回 bool
  • 后置 a++ a--(旧值副本)
  • 类型转换:返回目标类型值

3) “该返回值却返回引用”会出事吗?

通常会:

  • 悬空引用:返回局部临时变量的引用
  • 语义错误:把本应不修改操作数的 + 变成类似 +=

成员 vs 非成员怎么选(最实用判断)

  1. 语法规定必须成员= [] () ->
  2. 修改左操作数(复合赋值):优先成员(+= 等)
  3. 需要对称性/隐式转换(+ == < 等):优先非成员
  4. 左操作数不是你的类型(流 << >>:必须非成员(通常 friend)

易错点清单

  • operator+ 内部写 return lhs + rhs; 会无限递归
  • 忘记 constT operator+(...) const(成员版 + 应是 const)
  • >> 的对象参数误写成 const T&(输入必须改对象)
  • operator[] 只写一个版本导致 const 对象无法下标访问
  • 返回类型写错:+= 返回值会破坏链式语义/增加拷贝

9. 模板与泛型编程

├── 9.1 函数模板
│   ├── 模板定义
│   ├── 模板实例化
│   ├── 模板参数
│   ├── 模板特化
│   └── 模板重载
├── 9.2 类模板
│   ├── 类模板定义
│   ├── 模板成员函数
│   ├── 模板参数默认值
│   └── 模板特化与偏特化
└── 9.3 模板高级特性
    ├── 可变参数模板(C++11)
    ├── 模板元编程基础
    └── 类型 traits

第 9 章 模板记忆手册(以 template <typename R, typename... Ts> 为核心)

记忆锚点:R = 我额外想指定/控制的类型(常见:返回类型/策略类型);Ts... = 从实参推导出来的一串类型
一句话:模板=把“类型/常量/行为”提到编译期参数,让编译器生成对应版本代码。


9.1 函数模板

9.1.1 模板定义

  • 语法骨架:
  • template <typename T> ...:单类型参数
  • template <typename T, typename U> ...:多类型参数
  • template <typename R, typename... Ts> ...:一个“独立类型” + 可变参数包
  • typenameclass 在模板参数处等价。

模板参数的三种常见形态

  • 类型参数:typename T
  • 非类型参数:int N(编译期常量)
  • 参数包:typename... Ts(可变数量的类型)

9.1.2 模板实例化(编译器生成代码)

  • 模板本身不是“已生成的函数”,被调用时才实例化
  • 两种触发方式:
  • 隐式实例化:f(1, 2.5) 编译器推导类型并生成版本
  • 显式实例化/指定:f<double>(1, 2.5)

记忆点

  • 推导失败常见原因:同一个模板参数要求类型一致(如 template<typename T> f(T,T) 传入 int+double

9.1.3 模板参数(为何要 R + Ts...

template <typename R, typename... Ts> 的意义

  • R:你要“明确指定”的类型(常见:返回类型、策略类型、目标类型)
  • Ts...:由调用处实参自动推导的一串类型(数量可变)

典型用途(必须会)

  • 强制/控制返回类型(避免溢出、提升精度、统一输出)
  • 让调用者注入“策略类型”(比较器、分配器、格式化器)

可变参数 + 折叠(C++17 重点)

  • 折叠表达式:(expr op ...) / (... op expr)
  • 常见:((cout << xs), ...)

9.1.4 模板特化(Specialization)

函数模板

  • 允许:全特化
  • 不允许:偏特化(用重载替代)

记忆句:

  • 函数模板:特化少用,重载更常用;偏特化不存在。

9.1.5 模板重载(Overload)

  • 可以同时存在:
  • 普通函数重载
  • 函数模板重载
  • 选择规则(记忆版):
  • 能匹配更“精确”的版本优先(常见:普通函数可能优先于模板)
  • 模板之间看更特化/更匹配的那个

9.2 类模板

9.2.1 类模板定义

  • 目的:把“数据结构/类”做成按类型生成的版本(容器、智能指针、工具类)。
  • 典型形态:
  • template<typename T> class Box;
  • template<typename T, int N> class Array;(N 为编译期常量)

记忆句:

  • 类模板 = 同一份类设计,按类型/编译期参数生成不同类。

9.2.2 模板成员函数

  • 类模板的成员函数也依赖模板参数。
  • 实战高频坑:
  • 定义常放在头文件/同一翻译单元,否则可能链接不到实例化代码。

9.2.3 模板参数默认值

  • 形态:
  • template<typename T=int> class Box;
  • 用法:
  • Box<> 等价于 Box<int>

9.2.4 特化与偏特化

类模板

  • 支持:全特化、偏特化(函数模板做不到)

记忆句:

  • 偏特化是类模板的专属武器。

9.3 模板高级特性

9.3.1 可变参数模板(C++11)

  • template<typename... Ts>:接收任意数量类型
  • Ts... xs:接收任意数量实参
  • 展开方式:
  • C++11/14:递归展开
  • C++17:折叠表达式(更简洁、优先掌握)

template <typename R, typename... Ts> 的结合点:

  • R 用来指定结果/策略,Ts... 用来接收任意实参

9.3.2 模板元编程基础

  • 思想:编译期计算/编译期分支(constexpr、模板递归、static_assert
  • 记忆点:很多“检查”可以提前到编译期完成。

9.3.3 类型 traits

  • 作用:在编译期判断类型性质、选择实现或限制类型
  • 常用:
  • std::is_integral_v<T>
  • std::is_arithmetic_v<T>
  • std::is_same_v<A,B>
  • std::common_type_t<T,U>

R, Ts... 的关系(记忆句):

  • traits 帮你“约束 Ts… 是否可用”,common_type 帮你“推导合理的返回类型”。

速背:什么时候要 template <typename R, typename... Ts>

  • 你希望调用者显式指定一个“结果/策略类型”:R
  • 同时还要接收任意多个、类型可能各不相同的参数:Ts...

典型题型(背下来)

  • “把任意参数统一转换成 R 再处理”
  • “日志/打印/格式化:参数个数不定”
  • “构造转发:把 Ts… 完整转发给某个构造函数”(更偏工程)

终极对照表(最常考的三种模板头)

  • template <typename T>:一类参数,要求同型
  • template <typename T, typename U>:两类参数,允许不同型(常配 common_type_t
  • template <typename R, typename... Ts>:一个“指定类型” + 一串“推导类型”(最灵活)

10. 异常处理

├── 10.1 异常处理基础
│   ├── try-catch块-/  
│   ├── throw语句
│   └── 异常类型
├── 10.2 异常规范
│   ├── 异常说明
│   └── noexcept(C++11)
└── 10.3 标准异常类
    └── 异常类层次结构

11. 文件操作

├── 11.1 文件流类
│   ├── ifstream
│   ├── ofstream
│   └── fstream
├── 11.2 文件操作
│   ├── 打开与关闭文件
│   ├── 文本文件读写
│   └── 二进制文件读写
└── 11.3 文件指针操作
    ├── seekg/seekp
    └── tellg/tellp

include

include

include

using namespace std;

struct Data { int a; double b; };

int main() {
// 文本写
{
ofstream out(“a.txt”);
out << “hello\n” << 123 << “\n”;
}

// 文本读(按行)
{
    ifstream in("a.txt");
    string line;
    while (getline(in, line)) cout << line << "\n";
}

// 二进制写/读
{
    Data w{1, 2.5};
    ofstream out("data.bin", ios::binary);
    out.write(reinterpret_cast<const char*>(&w), sizeof(w));

    ifstream in("data.bin", ios::binary);
    Data r{};
    in.read(reinterpret_cast<char*>(&r), sizeof(r));
    cout << r.a << " " << r.b << "\n";
}

// 文件大小
{
    ifstream in("data.bin", ios::binary);
    in.seekg(0, ios::end);
    cout << "size=" << in.tellg() << " bytes\n";
}

}

12. 标准库STL

├── 12.1 容器
│   ├── 序列容器
│   │   ├── vector
│   │   ├── deque
│   │   ├── list
│   │   ├── forward_list(C++11)
│   │   └── array(C++11)
│   ├── 关联容器
│   │   ├── set/multiset
│   │   ├── map/multimap
│   │   ├── unordered_set(C++11)
│   │   └── unordered_map(C++11)
│   └── 容器适配器
│       ├── stack
│       ├── queue
│       └── priority_queue
├── 12.2 迭代器
│   ├── 输入/输出迭代器
│   ├── 前向迭代器
│   ├── 双向迭代器
│   ├── 随机访问迭代器
│   └── 迭代器适配器
├── 12.3 算法
│   ├── 非修改序列算法
│   ├── 修改序列算法
│   ├── 排序及相关操作
│   ├── 数值算法
│   └── 函数对象
├── 12.4 函数对象与Lambda
│   ├── 内置函数对象
│   ├── 函数适配器
│   └── bind(C++11)
└── 12.5 字符串类(string)
    ├── 字符串操作
    ├── 字符串与C风格字符串
    └── 字符串流

13. cpp11/14/17新特性

├── 13.1 自动类型推导
│   ├── auto
│   └── decltype
├── 13.2 智能指针
├── 13.3 右值引用与移动语义
│   ├── 右值引用
│   ├── 移动构造函数
│   └── 移动赋值运算符
├── 13.4 Lambda表达式
├── 13.5 范围for循环
├── 13.6 初始化列表
│   └── 统一初始化语法
├── 13.7 类型相关
│   ├── nullptr
│   ├── 强类型枚举
│   └── 类型别名模板
├── 13.8 并发支持(了解)
│   ├── thread
│   ├── mutex
│   └── atomic
└── 13.9 其他特性
    ├── constexpr
    ├── static_assert
    ├── 委托构造函数
    └── 继承构造函数

14. 内存管理

├── 14.1 内存分区
│   ├── 栈区
│   ├── 堆区
│   ├── 全局/静态存储区
│   ├── 常量存储区
│   └── 代码区
├── 14.2 内存分配方式
│   ├── 静态分配
│   ├── 栈分配
│   └── 堆分配
└── 14.3 常见内存问题
    ├── 内存泄漏
    ├── 缓冲区溢出
    ├── 野指针
    └── 重复释放

本技术内容仅供学习和交流使用,如有疑问请联系qq2014160588并注明来意。请确保在使用过程中遵守相关法律法规。任何因使用本技术内容而导致的直接或间接损失,作者概不负责。用户需自行承担因使用本技术内容而产生的所有风险和责任。请勿将本技术内容用于任何非法用途。
上一篇