C++知识点梳理

1. C++ 98 基础

1.1 数组

1.1.1 一维数组

概念:一块连续内存,存放相同类型数据。

数组长度计算:sizeof(数组) / sizeof(数组[0])

示例:数组倒置 双下标法(倒置核心)

  • start 指向开头
  • end 指向结尾
  • 交换,然后向中间靠拢

循环交换while(start < end)

代码示例:

#include <iostream>
using namespace std;

int main() {
    int arr[] = {1, 3, 5, 7, 9};
    int len = sizeof(arr) / sizeof(arr[0]); // 数组长度

    int start = 0;
    int end = len - 1;

    // 数组倒置
    while (start < end) {
        // 交换
        int temp = arr[start];
        arr[start] = arr[end];
        arr[end] = temp;

        start++;
        end--;
    }

    // 输出
    for (int i = 0; i < len; i++) {
        cout << arr[i] << endl;
    }
    return 0;
}
1.1.2 二维数组

概念:数组的数组,可以理解成表格。

定义格式:数据类型 数组名[行][列]

行数计算:sizeof(arr) / sizeof(arr[0])

列数计算:sizeof(arr[0]) / sizeof(arr[0][0])

下标访问:arr[行][列]

代码示例:

#include <iostream>
using namespace std;

int main() {
    // 2行3列
    int arr[2][3] = {
        {1, 2, 3},
        {4, 5, 6}
    };

    // 遍历
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            cout << arr[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

1.2 指针

1.2.1 指针

概念:用来存地址的变量。

定义:数据类型 *指针名

取地址:&变量名

解引用:*指针名 → 拿到地址里的值

空指针:int *p = NULL; 不能访问

const修饰指针

  1. const int *p:值不能改,指向可以改
  2. int *const p:指向不能改,值可以改
  3. const int *const p:都不能改

代码示例:

#include <iostream>
using namespace std;

int main() {
    int a = 10;
    int *p = &a;  // 指针存a的地址

    cout << *p << endl; // 解引用,输出10
    *p = 100;           // 修改值
    cout << a << endl;  // 输出100

    // const 指针示例
    const int *p1 = &a;  // 值不可改
    int *const p2 = &a;  // 指向不可改
    return 0;
}
1.2.2 指针操作数组(冒泡排序)

知识点:数组名 = 数组首地址

指针访问数组元素:arr[i]等价于*(arr + i)

函数 + 指针 + 数组:数组传参时会退化为指针。

注意指针做函数参数,可以直接修改原数组

代码示例:

#include <iostream>
using namespace std;

// 指针操作数组
void bubbleSort(int *arr, int len) {
    for (int i = 0; i < len - 1; i++) {
        for (int j = 0; j < len - i - 1; j++) {
            // *(arr+j) 等价 arr[j]
            if (*(arr + j) > *(arr + j + 1)) {
                int temp = *(arr + j);
                *(arr + j) = *(arr + j + 1);
                *(arr + j + 1) = temp;
            }
        }
    }
}

int main() {
    int arr[] = {3, 1, 4, 2};
    int len = sizeof(arr) / sizeof(arr[0]);

    bubbleSort(arr, len); // 数组名=首地址

    for (int i = 0; i < len; i++) {
        cout << arr[i] << " ";
    }
    return 0;
}

1.3 结构体

概念:结构体是自定义数据类型,可封装多个不同类型的数据;支持嵌套、数组、指针、函数参数传递

注意点

  1. 结构体传参:

    • 值传递:形参是结构体副本,修改形参不影响实参;

    • 地址传递:形参是结构体指针,修改形参影响实参(加const可禁止修改);

  2. 结构体指针访问成员:->(替代.);

  3. typedef可简化结构体类型名。

代码示例:

#include <iostream>
#include <string>
using namespace std;

// 嵌套结构体:老师
struct Teacher {
    int id;
    string name;
};

// 简化结构体类型名
typedef struct {
    int id;
    string name;
    int age;
    Teacher tea; // 嵌套老师结构体
} Student;

// 值传递:不修改实参
void print_stu(Student stu) {
    stu.age = 99; // 修改副本,不影响实参
    cout << "值传递:" << stu.id << " " << stu.name << " " << stu.age << endl;
}

// 地址传递:可修改实参(加const禁止修改)
void print_stu_ptr(const Student *stu) {
    // stu->age = 88; // 错误:const禁止修改
    cout << "地址传递:" << stu->id << " " << stu->name << " " << stu->age << endl;
    cout << "嵌套老师:" << stu->tea.id << " " << stu->tea.name << endl;
}

int main() {
    // 结构体初始化
    Student stu = {1, "小明", 18, {1001, "张老师"}};
    
    // 值传递
    print_stu(stu); // 输出age=99(副本)
    // 实参age仍为18
    cout << "实参age:" << stu.age << endl;

    // 地址传递
    print_stu_ptr(&stu);

    // 结构体数组
    Student stu_arr[2] = {
        {2, "小红", 17, {1001, "张老师"}},
        {3, "小刚", 19, {1002, "李老师"}}
    };
    for (int i = 0; i < 2; i++) {
        cout << "数组第" << i << "个:" << stu_arr[i].name << endl;
    }

    return 0;
}

1.4 内存分区

概念:C++ 程序运行时,内存分为 4 个核心区域,不同区域数据的生命周期和管理方式不同:

分区 生命周期 存储内容 管理方式
代码区 程序运行前 二进制可执行代码、常量 操作系统管理
全局区 程序运行前 全局变量、静态变量(static)、常量(全局 const / 字符串常量) 程序结束后操作系统释放
栈区 程序运行后 局部变量、函数参数、返回值 编译器自动分配 / 释放(出作用域销毁)
堆区 程序运行后 程序员手动开辟的数据 程序员通过new开辟、delete释放;未释放则程序结束后 OS 回收

注意点:

  1. 栈区不能返回局部变量的地址(局部变量出函数栈帧即销毁,地址变为野地址);
  2. 堆区数据需手动管理,避免内存泄漏;
  3. const修饰的局部变量存在栈区,const修饰的全局变量存在全局区。

代码示例:

#include <iostream>
using namespace std;

// 全局变量(全局区)
int g_num = 10;
// 全局const常量(全局区)
const int g_const = 20;

// 堆区开辟数据的函数
int* func_heap() {
    // new将数据开辟到堆区,返回指针
    int* p = new int(30); 
    return p; // 堆区地址可返回(数据未销毁)
}

// 错误示例:返回栈区局部变量地址
int* func_stack_error() {
    int num = 40; // 栈区局部变量
    return &num;  // 警告:返回栈区地址,出函数后num销毁
}

int main() {
    // 局部变量(栈区)
    int local_num = 50;
    // 静态变量(全局区)
    static int static_num = 60;
    // 局部const常量(栈区)
    const int local_const = 70;

    // 打印各区域变量地址(可观察地址分布规律)
    cout << "全局变量地址:" << (long long)&g_num << endl;
    cout << "全局const地址:" << (long long)&g_const << endl;
    cout << "静态变量地址:" << (long long)&static_num << endl;
    cout << "字符串常量地址:" << (long long)&"hello" << endl;
    cout << "局部变量地址:" << (long long)&local_num << endl;
    cout << "局部const地址:" << (long long)&local_const << endl;

    // 堆区数据使用
    int* heap_p = func_heap();
    cout << "堆区数据值:" << *heap_p << endl;
    delete heap_p; // 释放堆区数据

    // 栈区地址错误示例(运行可能出现随机值)
    // int* stack_p = func_stack_error();
    // cout << *stack_p << endl; // 非法访问

    return 0;
}
1.4.1 new / delete 关键字

概念:

  1. new:用于在堆区手动开辟内存,返回对应数据类型的指针;
  2. delete:释放new开辟的单个数据内存;
  3. delete[]:释放new[]开辟的数组内存(必须配套使用,否则内存泄漏)。

注意点:

  1. new int(10):开辟单个 int 内存,初始值为 10;
  2. new int[10]:开辟长度为 10 的 int 数组内存;
  3. 释放后的内存不可再次访问(非法内存访问)。

代码示例:

#include <iostream>
using namespace std;

// 堆区开辟单个数据
int* create_single() {
    return new int(100); // 初始值100
}

// 堆区开辟数组
int* create_array() {
    int* arr = new int[5]; // 长度为5的int数组
    for (int i = 0; i < 5; i++) {
        arr[i] = i * 10; // 赋值:0,10,20,30,40
    }
    return arr;
}

int main() {
    // 单个数据操作
    int* p1 = create_single();
    cout << "堆区单个数据:" << *p1 << endl;
    delete p1; // 释放单个数据
    // cout << *p1 << endl; // 错误:释放后访问

    // 数组操作
    int* p2 = create_array();
    for (int i = 0; i < 5; i++) {
        cout << "数组第" << i << "个值:" << p2[i] << endl;
    }
    delete[] p2; // 释放数组(必须加[])

    return 0;
}
1.4.2 引用

概念:引用是变量的别名,本质是 “指针常量”(指向不可改,值可改),语法:数据类型 &别名 = 原变量;

关键点:

  1. 引用必须初始化int &a; 错误);
  2. 初始化后不可更改指向(只能绑定一个原变量);
  3. 引用做函数参数:可直接修改实参(替代指针,简化操作);
  4. 引用做返回值:不可返回局部变量的引用(栈区数据销毁),可返回静态变量 / 堆区数据的引用;
  5. 常量引用:const int &a = 10;,用于保护数据不被修改,常作为函数参数。

代码示例:

#include <iostream>
using namespace std;

// 引用做函数参数:交换两个数
void swap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}

// 引用做返回值:返回静态变量(全局区,不会销毁)
int& get_static() {
    static int num = 200;
    return num;
}

// 常量引用:保护参数不被修改
void print(const int &a) {
    // a = 300; // 错误:常量引用不可修改
    cout << "常量引用值:" << a << endl;
}

int main() {
    // 基础引用
    int num = 10;
    int &alias = num; // 初始化引用
    cout << "原变量:" << num << " 引用别名:" << alias << endl;
    alias = 100; // 修改别名=修改原变量
    cout << "修改后原变量:" << num << endl;

    // 引用交换函数
    int a = 10, b = 20;
    swap(a, b);
    cout << "交换后:a=" << a << " b=" << b << endl;

    // 引用返回值(可作为左值)
    int &ref = get_static();
    get_static() = 300; // 函数调用作为左值
    cout << "静态变量引用:" << ref << endl;

    // 常量引用
    const int &c_ref = 50; // 直接绑定常量(编译器自动生成临时变量)
    print(c_ref);

    return 0;
}
1.4.3 函数进阶

**函数默认参数:**为函数参数指定默认值,调用时若不传该参数则使用默认值,传参则覆盖默认值。

  1. 若某个参数指定默认值,其后所有参数都必须有默认值
  2. 函数声明和实现不能同时指定默认值(二选一)。

代码示例:

#include <iostream>
using namespace std;

// 默认参数:b默认2,c默认3
int add(int a, int b = 2, int c = 3) {
    return a + b + c;
}

int main() {
    cout << "传1个参数:" << add(1) << endl;    // 1+2+3=6
    cout << "传2个参数:" << add(1, 10) << endl; // 1+10+3=14
    cout << "传3个参数:" << add(1, 10, 20) << endl; // 1+10+20=31
    return 0;
}

**函数占位参数:**函数参数只写类型,不写变量名(占位),调用时必须传入对应类型的参数(仅占位置,无实际用途)。

代码示例:

#include <iostream>
using namespace std;

// 占位参数:第二个int是占位
int func(int &a, int) {
    return a > 10 ? 100 : 200;
}

int main() {
    int num = 15;
    // 第二个参数必须传(占位)
    cout << func(num, 0) << endl; // num>10,返回100
    return 0;
}

**函数重载:**同一作用域下,函数名相同,参数类型 / 个数 / 顺序不同,可实现重载(提高复用性)。

  1. 返回值不能作为重载条件
  2. 引用可作为重载条件(int &a vs const int &a);
  3. 重载避免搭配默认参数(可能导致二义性)。

代码示例:

#include <iostream>
using namespace std;

// 重载1:无参数
void show() {
    cout << "无参数重载" << endl;
}

// 重载2:int&参数
void show(int &a) {
    a += 5;
    cout << "int&参数重载:" << a << endl;
}

// 重载3:const int&参数
void show(const int &a) {
    cout << "const int&参数重载:" << a << endl;
}

// 重载4:两个int参数(个数不同)
void show(int a, int b) {
    cout << "两个int参数重载:" << a + b << endl;
}

int main() {
    int num = 10;
    show();          // 调用无参数版
    show(num);       // 调用int&版(num是普通变量)
    show(20);        // 调用const int&版(字面量是常量)
    show(10, 20);    // 调用两个int参数版
    return 0;
}

1.5 面向对象

1.5.1 类与对象:
  1. :自定义数据类型,封装了属性(成员变量)和行为(成员函数 / 方法),是对象的模板。
  2. 对象:类的实例化,占用实际内存空间,可调用类的属性和方法。
  3. struct 与 class 的区别:默认访问权限不同(struct 默认public,class 默认private)。
1.5.2 访问权限(三大访问控制符)
权限 类内访问 类外访问 继承中子类访问
public
private
protected

代码示例:

#include <iostream>
#include <string>
using namespace std;

class Person {
private:
    string m_id; // 私有属性:类外不可访问
protected:
    string m_car; // 保护属性:类外不可访问,子类可访问
public:
    string m_name; // 公有属性:类内外均可访问

    // 类内可访问所有权限的属性
    void setInfo(string name, string id, string car) {
        m_name = name;
        m_id = id;
        m_car = car;
    }

    void showInfo() {
        cout << "姓名:" << m_name << " 身份证:" << m_id << " 车辆:" << m_car << endl;
    }
};

int main() {
    Person p;
    p.m_name = "张三"; // 公有属性可直接访问
    // p.m_id = "123456"; // 错误:私有属性类外不可访问
    // p.m_car = "宝马"; // 错误:保护属性类外不可访问
    p.setInfo("张三", "123456789", "宝马"); // 通过公有方法访问私有/保护属性
    p.showInfo();
    return 0;
}
1.5.3 构造函数与析构函数
函数类型 语法特点 调用时机
构造函数 类名(参数列表){},无返回值(不写void),可重载(有参 / 无参 / 拷贝) 对象创建时自动调用,仅一次
析构函数 ~类名(){},无参数、无返回值,不可重载 对象销毁时自动调用,仅一次

构造函数分类:

  1. 无参构造:默认构造(不写时编译器自动生成);
  2. 有参构造:初始化对象属性;
  3. 拷贝构造类名(const 类名 &obj),用已有对象初始化新对象,解决浅拷贝问题。

初始化列表:

  1. 语法:构造函数(参数列表) : 属性1(值1), 属性2(值2)... {}
  2. 作用:直接初始化成员属性(尤其适合 const 属性、类类型成员)。

类对象作为类成员:

  1. 构造顺序:先构造成员对象,再构造自身;
  2. 析构顺序:先析构自身,再析构成员对象。

代码示例:

#include <iostream>
#include <string>
using namespace std;

// 成员类(手机)
class Phone {
public:
    string m_brand;
    // 有参构造
    Phone(string brand) : m_brand(brand) {
        cout << "Phone构造函数调用:" << m_brand << endl;
    }
    // 析构函数
    ~Phone() {
        cout << "Phone析构函数调用:" << m_brand << endl;
    }
};

// 主类(人)
class Person {
public:
    string m_name;
    Phone m_phone; // 类对象作为成员

    // 初始化列表初始化属性
    Person(string name, string phoneBrand) : m_name(name), m_phone(phoneBrand) {
        cout << "Person构造函数调用:" << m_name << endl;
    }

    // 拷贝构造(深拷贝示例)
    Person(const Person &p) {
        m_name = p.m_name;
        m_phone = Phone(p.m_phone.m_brand); // 重新创建对象,避免浅拷贝
        cout << "Person拷贝构造函数调用" << endl;
    }

    ~Person() {
        cout << "Person析构函数调用:" << m_name << endl;
    }
};

void test() {
    // 普通构造 + 初始化列表
    Person p1("李四", "华为");
    cout << "姓名:" << p1.m_name << " 手机:" << p1.m_phone.m_brand << endl;

    // 拷贝构造
    Person p2 = p1;
    cout << "拷贝对象:" << p2.m_name << " 手机:" << p2.m_phone.m_brand << endl;
}

int main() {
    test();
    return 0;
}

输出:

Phone构造函数调用:华为
Person构造函数调用:李四
姓名:李四 手机:华为
Phone构造函数调用:华为
Person拷贝构造函数调用
拷贝对象:李四 手机:华为
Person析构函数调用:李四
Phone析构函数调用:华为
Person析构函数调用:李四
Phone析构函数调用:华为
1.5.4 浅拷贝与深拷贝

概念:

  1. 浅拷贝:编译器默认的拷贝方式,仅简单赋值(指针指向同一内存地址,易导致重复释放);
  2. 深拷贝:手动在堆区重新开辟内存,拷贝数据(解决浅拷贝的内存泄漏 / 重复释放问题)。

代码示例:

#include <iostream>
using namespace std;

class Person {
public:
    int m_age;
    int *m_height; // 堆区指针

    // 有参构造
    Person(int age, int height) {
        m_age = age;
        m_height = new int(height); // 堆区分配内存
        cout << "有参构造调用" << endl;
    }

    // 深拷贝构造函数(关键)
    Person(const Person &p) {
        m_age = p.m_age;
        // 浅拷贝:m_height = p.m_height; (错误,会导致重复释放)
        m_height = new int(*p.m_height); // 重新开辟堆区内存,拷贝值
        cout << "深拷贝构造调用" << endl;
    }

    ~Person() {
        // 释放堆区内存
        if (m_height != NULL) {
            delete m_height;
            m_height = NULL; // 防止野指针
        }
        cout << "析构函数调用" << endl;
    }
};

void test() {
    Person p1(20, 180);
    cout << "p1年龄:" << p1.m_age << " 身高:" << *p1.m_height << endl;

    Person p2 = p1; // 调用拷贝构造
    cout << "p2年龄:" << p2.m_age << " 身高:" << *p2.m_height << endl;
}

int main() {
    test();
    return 0;
}
1.5.5 面向对象的三大特性(封装/继承/多态)

概念:封装

  1. 概念:将属性和行为封装到类中,通过访问权限控制读写,提高代码安全性;
  2. 优势:可控制属性读写权限、检测输入有效性。

代码示例:

#include <iostream>
#include <string>
using namespace std;

class Student {
private:
    string m_name; // 私有属性:仅类内访问
    int m_age;     // 私有属性:控制读写

public:
    // 设置姓名(可控写)
    void setName(string name) {
        m_name = name;
    }

    // 获取姓名(可控读)
    string getName() {
        return m_name;
    }

    // 设置年龄(检测有效性)
    void setAge(int age) {
        if (age < 0 || age > 150) {
            cout << "年龄无效!" << endl;
            m_age = 0;
            return;
        }
        m_age = age;
    }

    // 获取年龄
    int getAge() {
        return m_age;
    }
};

int main() {
    Student s;
    s.setName("王五");
    s.setAge(200); // 无效年龄
    s.setAge(18);  // 有效年龄

    cout << "姓名:" << s.getName() << " 年龄:" << s.getAge() << endl;
    return 0;
}

概念:继承

**定义:**面向对象三大特性之一,允许子类(派生类)复用父类(基类)的属性和方法,减少代码冗余。

  1. 语法:class 子类 : 继承权限 父类 {}
  2. 继承权限:public(父类权限不变)、private(父类所有权限变为 private)、protected(父类 public 变为 protected)。

关键特性:

  1. 父类非静态成员都会被继承(编译器隐藏不可直接访问,但确实存在);
  2. 构造 / 析构顺序:父类构造 → 子类构造 → 子类析构 → 父类析构;
  3. 同名成员:子类优先访问自身,加父类::可访问父类同名成员;
  4. 多继承:class 子类 : public 父类1, public 父类2(实际开发不建议,易引发菱形继承问题)。

代码示例:

#include <iostream>
using namespace std;

// 基类
class Base {
public:
    int m_A = 200;
    Base() { cout << "父类构造" << endl; }
    ~Base() { cout << "父类析构" << endl; }
    void func() { cout << "父类func" << endl; }
};

// 子类(公共继承)
class Son : public Base {
public:
    int m_A = 100; // 同名成员
    Son() { cout << "子类构造" << endl; }
    ~Son() { cout << "子类析构" << endl; }
    void func() { cout << "子类func" << endl; }
};

// 多继承示例
class Base1 { public: int m_a = 100; };
class Base2 { public: int m_b = 200; };
class MultiSon : public Base1, public Base2 { public: int m_c = 300; };

int main() {
    // 基础继承
    Son s;
    cout << "子类m_A: " << s.m_A << endl;        // 100
    cout << "父类m_A: " << s.Base::m_A << endl; // 200
    s.func();          // 子类func
    s.Base::func();    // 父类func

    // 多继承
    MultiSon ms;
    cout << ms.m_a << " " << ms.m_b << " " << ms.m_c << endl; // 100 200 300

    return 0;
}

概念:多态

**分类: ** 静态多态:编译期确定(函数重载、运算符重载); 动态多态:编译期确定(函数重载、运算符重载);

动态多态条件: 有继承关系; 子类重写父类虚函数(返回值、函数名、参数完全一致)。

动态多态使用:父类指针 / 引用指向子类对象。

纯虚函数 / 抽象类

  1. 纯虚函数:virtual 返回值 函数名() = 0
  2. 抽象类:包含纯虚函数的类,无法实例化,子类必须重写纯虚函数(否则也为抽象类)。

代码示例:

#include <iostream>
using namespace std;

// 抽象计算器类(纯虚函数)
class AbstractCalculator {
public:
    int num1, num2;
    virtual int getResult() = 0; // 纯虚函数
};

// 加法计算器
class AddCalculator : public AbstractCalculator {
public:
    int getResult() override { return num1 + num2; }
};

// 减法计算器
class SubCalculator : public AbstractCalculator {
public:
    int getResult() override { return num1 - num2; }
};

int main() {
    // 父类指针指向子类对象(动态多态)
    AbstractCalculator* calc = new AddCalculator;
    calc->num1 = 100; calc->num2 = 20;
    cout << "加法结果: " << calc->getResult() << endl; // 120
    delete calc;

    calc = new SubCalculator;
    calc->num1 = 100; calc->num2 = 20;
    cout << "减法结果: " << calc->getResult() << endl; // 80
    delete calc;

    return 0;
}
1.5.6 运算符重载

**概念:**重新定义运算符的行为,使其能作用于自定义类型(如类对象)。

实现方式

  • 成员函数:返回值 operator运算符(参数)
  • 全局函数:返回值 operator运算符(参数1, 参数2)

常用重载场景

  • 算术运算符(+、-、*、/);
  • 左移运算符(<<,用于输出自定义类型);
  • 递增 / 递减(++、–,区分前置 / 后置);
  • 赋值运算符(=,解决浅拷贝问题);
  • 关系运算符(==、!=);
  • 函数调用运算符((),仿函数)。

代码示例:

#include <iostream>
#include <string>
using namespace std;

class Person {
    friend ostream& operator<<(ostream& cout, Person& p); // 友元支持全局函数访问私有成员
public:
    int m_num = 0;
    string m_name = "Tom";
    int* m_age = new int(18); // 堆区属性(用于赋值重载)

    // 加号重载(成员函数)
    Person operator+(const Person& p) {
        Person temp;
        temp.m_num = this->m_num + p.m_num;
        return temp;
    }

    // 前置递增重载
    Person operator++() {
        ++m_num;
        return *this;
    }

    // 后置递增重载(int为占位参数)
    Person operator++(int) {
        Person temp = *this;
        m_num++;
        return temp;
    }

    // 赋值运算符重载(解决浅拷贝)
    Person& operator=(const Person& p) {
        if (m_age) { delete m_age; m_age = nullptr; } // 释放原有内存
        m_age = new int(*p.m_age); // 深拷贝
        m_num = p.m_num;
        return *this;
    }

    // 关系运算符重载(==)
    bool operator==(const Person& p) {
        return (m_name == p.m_name && *m_age == *p.m_age);
    }
};

// 左移运算符重载(全局函数)
ostream& operator<<(ostream& cout, Person& p) {
    cout << "num: " << p.m_num << ", age: " << *p.m_age;
    return cout;
}

// 函数调用运算符重载(仿函数)
class MyPrint {
public:
    void operator()(string s) { cout << s << endl; }
};

int main() {
    // 加号重载
    Person p1, p2;
    p1.m_num = 10; p2.m_num = 20;
    Person p3 = p1 + p2;
    cout << "p3.num: " << p3.m_num << endl; // 30

    // 递增重载
    Person p4;
    ++p4;
    cout << "前置++: " << p4 << endl; // num:1, age:18
    p4++;
    cout << "后置++: " << p4 << endl; // num:2, age:18

    // 赋值重载
    Person p5;
    *p5.m_age = 28;
    p1 = p5;
    cout << "p1.age: " << *p1.m_age << endl; // 28

    // 关系运算符
    cout << (p1 == p5) << endl; // 1(true)

    // 仿函数
    MyPrint print;
    print("Hello Operator Overload!"); // Hello Operator Overload!

    return 0;
}
1.5.7 虚析构与纯虚析构

问题背景:多态场景下,父类指针释放子类对象时,默认只调用父类析构,子类堆区属性无法释放(内存泄漏)。

解决方法

  • 虚析构:virtual ~父类() {}
  • 纯虚析构:virtual ~父类() = 0;(需类外实现);

区别:纯虚析构会让类成为抽象类,无法实例化;虚析构则不会

代码示例:

#include <iostream>
#include <string>
using namespace std;

class Base {
public:
    Base() { cout << "父类构造" << endl; }
    // 纯虚析构(类内声明,类外实现)
    virtual ~Base() = 0;
    virtual void speak() = 0; // 纯虚函数
};

// 纯虚析构实现
Base::~Base() { cout << "父类纯虚析构" << endl; }

class Son : public Base {
public:
    string* name;
    Son(string s) {
        name = new string(s);
        cout << "子类构造" << endl;
    }
    ~Son() {
        if (name) {
            delete name;
            name = nullptr;
            cout << "子类析构" << endl;
        }
    }
    void speak() override { cout << *name << " 说话" << endl; }
};

int main() {
    Base* b = new Son("小明");
    b->speak(); // 小明 说话
    delete b;   // 触发子类析构 + 父类纯虚析构,避免内存泄漏

    return 0;
}
1.5.8 静态成员

概念:静态成员分为静态成员变量静态成员函数,核心是 “类级共享”,不属于单个对象。

静态成员变量:

  1. 所有对象共享同一份数据;
  2. 编译阶段分配内存(全局区);
  3. 类内声明,类外初始化(数据类型 类名::变量名 = 初始值);
  4. 受访问权限(public/private)限制。

静态成员函数:

  1. 所有对象共享同一个函数;
  2. 只能访问静态成员变量(无 this 指针,无法访问非静态成员);
  3. 可通过对象名.函数名类名::函数名调用。

代码示例:

#include <iostream>
using namespace std;

class Person
{
private:
    static int m_B; // 私有静态成员变量(类外无法直接访问)
public:
    static int m_A; // 公有静态成员变量

    // 静态成员函数:只能访问静态成员变量
    static void func() {
        m_A = 300; // 允许:访问静态成员
        // m_B = 200; // 允许:类内可访问私有静态成员
        cout << "静态函数调用,m_A = " << m_A << endl;
    }
};

// 静态成员变量类外初始化
int Person::m_A = 100;
int Person::m_B = 200;

void test() {
    // 1. 静态成员变量访问
    Person p1;
    cout << "对象访问m_A:" << p1.m_A << endl; // 输出100
    Person p2;
    p2.m_A = 200; // 所有对象共享,修改后p1的m_A也变
    cout << "p1的m_A:" << p1.m_A << endl; // 输出200
    cout << "类名访问m_A:" << Person::m_A << endl; // 输出200

    // 2. 静态成员函数调用
    p1.func(); // 对象调用:输出m_A=300
    Person::func(); // 类名调用:输出m_A=300

    // cout << Person::m_B << endl; // 报错:私有静态成员不可访问
}

int main() {
    test();
    return 0;
}
1.5.9 对象模型与this指针

概念:对象模型

  1. 成员变量和成员函数分开存储
  2. 空对象占用1字节内存(编译器分配唯一地址,区分不同空对象);
  3. 静态成员变量不占用对象内存(属于类,全局共享)。

概念:this指针

每个非静态成员函数都隐含this指针,指向调用该函数的对象

用途:

  • 区分形参和成员变量同名(this->成员变量 = 形参);
  • 成员函数中返回对象本身(return *this),支持链式调用。

代码示例:

#include <iostream>
using namespace std;

// 1. 对象模型:成员变量/函数分开存储
class Person1
{
public:
    int m_a;
    static int m_c; // 静态成员变量(不占用对象内存)
    Person1() = default;
};
int Person1::m_c = 10; // 静态成员变量类外初始化

// 2. this指针:区分同名变量 + 链式调用
class Person2
{
public:
    int age;
    // 构造函数:this区分形参和成员变量
    Person2(int age) {
        this->age = age;
    }

    // 返回对象本身(引用返回,避免拷贝),支持链式调用
    Person2& addAge(Person2 &p) {
        this->age += p.age;
        return *this; // 返回当前对象
    }
};

void test1() {
    // 空对象内存:1字节
    Person1 p_empty;
    cout << "空对象内存:" << sizeof(p_empty) << endl; // 输出1

    // 含成员变量的对象内存:4字节(int占4字节)
    Person1 p;
    cout << "含成员变量的对象内存:" << sizeof(p) << endl; // 输出4
}

void test2() {
    // this指针链式调用
    Person2 p1(10);
    Person2 p2(20);
    // 链式调用:p2.age = 20 + 10 + 10 + 10 = 50
    p2.addAge(p1).addAge(p1).addAge(p1);
    cout << "p2最终年龄:" << p2.age << endl; // 输出50
}

int main() {
    test1();
    test2();
    return 0;
}
1.5.10 空指针访问成员函数

概念:空指针(NULL/nullptr)可以调用类的成员函数,但需注意:

  • 若函数未使用this指针(如仅输出固定字符串),调用不会报错;
  • 若函数使用this指针(如访问成员变量),会触发内存错误(this为 NULL);
  • 解决方案:函数内先判断this == NULL,避免访问空指针。

代码示例:

#include <iostream>
using namespace std;

class Person
{
public:
    int m_age;

    // 无this指针使用:空指针可调用
    void showClassName() {
        cout << "这是Person类" << endl;
    }

    // 有this指针使用:需判断this是否为空
    void showAge() {
        if (this == NULL) { // 健壮性判断
            cout << "指针为空,无法访问age" << endl;
            return;
        }
        cout << "年龄:" << this->m_age << endl;
    }
};

void test() {
    Person *p = NULL; // 空指针
    p->showClassName(); // 正常调用:输出"这是Person类"
    p->showAge(); // 触发判断:输出"指针为空,无法访问age"
}

int main() {
    test();
    return 0;
}
1.5.11 常函数与常对象

概念:

  1. 常函数:成员函数后加const修饰(返回值 函数名() const),本质是修饰this指针(const 类名* const this),限制函数内不能修改成员变量

    ​ 例外:mutable修饰的成员变量,可在常函数中修改。

  2. 常对象:对象前加constconst 类名 对象名),常对象只能调用常函数,且不能修改成员变量(除mutable变量)。

代码示例:

#include <iostream>
using namespace std;

class Person
{
public:
    int m_a; // 普通成员变量
    mutable int m_b; // 可在常函数/常对象中修改的变量

    // 常函数:不能修改普通成员变量
    void showPerson() const {
        // this->m_a = 100; // 报错:常函数禁止修改普通成员
        this->m_b = 200; // 允许:mutable变量可修改
        cout << "m_b = " << m_b << endl;
    }

    // 普通成员函数(常对象不能调用)
    void modifyA() {
        m_a = 100;
    }

    Person() = default;
    ~Person() = default;
};

void test() {
    // 常对象:只能调用常函数,不能修改普通成员
    const Person p;
    // p.m_a = 10; // 报错:常对象禁止修改普通成员
    p.m_b = 300; // 允许:mutable变量可修改
    p.showPerson(); // 允许:调用常函数

    // p.modifyA(); // 报错:常对象不能调用普通成员函数
}

int main() {
    test();
    return 0;
}
1.5.12 友元

概念:友元机制允许外部函数 / 类访问类的私有成员(private) 或保护成员(protected),打破类的封装性,适用于特殊场景(如跨类数据访问)。

友元的三种实现形式:

  1. 全局函数做友元:在类内用friend声明全局函数;
  2. 类做友元:在类内用friend class 类名声明友元类;
  3. 成员函数做友元:在类内用friend 返回值 类名::成员函数名(参数)声明友元成员函数。

代码示例:

#include <iostream>
#include <string>
using namespace std;

// 提前声明类(解决交叉引用)
class Building;

// 友元成员函数示例:GoodGay类的visit函数访问Building私有成员
class GoodGay
{
public:
    void visit(); // 声明访问函数
    Building *building; // 指向Building对象的指针
    GoodGay(); // 构造函数
};

class Building
{
    // 1. 全局函数做友元
    friend void good(Building *building);
    // 2. 类做友元(注释可放开测试)
    // friend class GoodGay;
    // 3. 成员函数做友元
    friend void GoodGay::visit();

private:
    string m_BedRoom = "卧室"; // 私有成员
public:
    string m_SittingRoom = "客厅"; // 公有成员
    Building() = default; // 默认构造
    ~Building() = default;
};

// GoodGay构造函数:初始化Building对象
GoodGay::GoodGay() {
    building = new Building;
}

// GoodGay成员函数:访问Building私有/公有成员
void GoodGay::visit() {
    cout << "成员函数做友元:访问" << building->m_BedRoom << endl;
    cout << "成员函数做友元:访问" << building->m_SittingRoom << endl;
}

// 全局函数做友元:访问Building私有/公有成员
void good(Building *building) {
    cout << "全局函数做友元:访问" << building->m_BedRoom << endl;
    cout << "全局函数做友元:访问" << building->m_SittingRoom << endl;
}

// 测试函数
void test() {
    // 全局函数友元测试
    Building b;
    good(&b);

    // 成员函数友元测试
    GoodGay g;
    g.visit();
}

int main() {
    test();
    return 0;
}

1.6 文件操作

1.6.1 核心概念

C++ 通过<fstream>头文件实现文件操作,核心流类:

  • ofstream:输出流(写文件),支持文本 / 二进制写入;
  • ifstream:输入流(读文件),支持文本 / 二进制读取;
  • fstream:输入输出流(读写文件)

文件打开方式:通过open()函数或构造函数指定,常用方式:

  • ios::in:读模式(默认 for ifstream);
  • ios::out:写模式(默认 for ofstream,会清空文件);
  • ios::app:追加模式(写在文件末尾);
  • ios::binary:二进制模式(默认是文本模式);
  • ios::trunc:清空文件(默认与 ios::out 配合);

文本文件:以字符形式存储,可直接编辑查看;

二进制文件:以字节形式存储,适合存储自定义类 / 结构体,不可直接编辑。

1.6.2 读写标准类型数据

代码示例:

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

// 写文本文件(追加模式)
void writeTextFile() {
    ofstream ofs;
    // 打开文件:路径 + 追加模式
    ofs.open("text.txt", ios::app);
    if (!ofs.is_open()) { // 判断是否打开成功
        cout << "文件打开失败!" << endl;
        return;
    }
    // 写入内容
    ofs << "文本文件测试:" << endl;
    ofs << "姓名:张三,年龄:20" << endl;
    ofs.close(); // 关闭流(必须)
    cout << "文本写入成功!" << endl;
}

// 读文本文件(三种常用方式)
void readTextFile() {
    ifstream ifs;
    ifs.open("text.txt", ios::in);
    if (!ifs.is_open()) {
        cout << "文件打开失败!" << endl;
        return;
    }

    // 方式1:字符数组读取
    char buf1[1024] = {0};
    cout << "=== 字符数组读取 ===" << endl;
    while (ifs.getline(buf1, sizeof(buf1))) {
        cout << buf1 << endl;
    }
    ifs.close();

    // 方式2:string读取(需重新打开)
    ifs.open("text.txt", ios::in);
    string buf2;
    cout << "=== string读取 ===" << endl;
    while (getline(ifs, buf2)) {
        cout << buf2 << endl;
    }
    ifs.close();
}

int main() {
    writeTextFile();
    readTextFile();
    return 0;
}
1.6.3 读写自定义数据类型数据

代码示例:

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

// 自定义类:需无指针成员(避免浅拷贝)
class Person {
public:
    char name[64]; // 用字符数组(string不适合二进制存储)
    int age;
    // 空构造(读文件时需要)
    Person() {}
    // 有参构造
    Person(const char *name, int age) {
        strcpy_s(this->name, name); // 安全字符串拷贝
        this->age = age;
    }
};

// 写二进制文件
void writeBinaryFile() {
    ofstream ofs;
    ofs.open("binary.dat", ios::binary | ios::app); // 二进制+追加
    if (!ofs.is_open()) {
        cout << "文件打开失败!" << endl;
        return;
    }
    Person p("李四", 28);
    // 写入:地址转char* + 类大小
    ofs.write((const char *)&p, sizeof(Person));
    ofs.close();
    cout << "二进制写入成功!" << endl;
}

// 读二进制文件
void readBinaryFile() {
    ifstream ifs;
    ifs.open("binary.dat", ios::binary | ios::in);
    if (!ifs.is_open()) {
        cout << "文件打开失败!" << endl;
        return;
    }
    Person p;
    // 读取:地址转char* + 类大小
    ifs.read((char *)&p, sizeof(Person));
    cout << "二进制读取结果:" << endl;
    cout << "姓名:" << p.name << ",年龄:" << p.age << endl;
    ifs.close();
}

int main() {
    writeBinaryFile();
    readBinaryFile();
    return 0;
}

1.7 模板(泛化编程)

概念:模板是 C++ 泛型编程的核心,允许编写与类型无关的代码,大幅提高代码复用性。分为函数模板类模板两类

  1. 泛型编程:编写不依赖具体数据类型的代码,仅用 “虚拟类型”(模板参数)指代类型,编译时根据实际传入类型生成具体代码。
  2. 模板参数:用template <class T>(或template <typename T>)声明,T为 “类型形参”,可理解为类型的 “占位符”。
  3. 模板实例化:使用模板时,编译器根据传入的具体类型,生成对应类型的函数 / 类(分为隐式实例化、显式实例化)

1.7.1 函数模板

**概念:**函数模板是通用的函数定义,参数类型、返回值类型可通过模板参数指定,支持不同类型的参数复用同一套逻辑。

关键特性:

  1. 自动类型推导:调用时编译器根据实参推导模板参数类型(需推导结果唯一);
  2. 显式指定类型:调用时通过<类型>强制指定模板参数类型;
  3. 与普通函数的区别:自动类型推导不支持隐式类型转换,显式指定类型支持;
  4. 模板重载:可定义参数个数 / 类型不同的同名函数模板;
  5. 模板具体化:针对特定类型(如自定义类)编写专属实现,优先级高于通用模板。

代码示例:

#include <iostream>
#include <string>
using namespace std;

// 1. 基础函数模板:交换两个值
template <class T>
void swapValue(T &a, T &b) {
    T temp = a;
    a = b;
    b = temp;
}

// 2. 模板重载:支持3个参数交换(示例)
template <class T>
void swapValue(T &a, T &b, T &c) {
    T temp = a;
    a = b;
    b = c;
    c = temp;
}

// 3. 模板局限性与具体化:通用对比函数 + 自定义类具体化
template <class T>
bool MyCompare(T &a, T &b) {
    return a == b;
}

// 自定义类
class Person {
public:
    int id;
    string name;
    Person(int id, string name) : id(id), name(name) {}
};

// 模板具体化:针对Person类的专属对比逻辑
template <> bool MyCompare(Person &a, Person &b) {
    return (a.id == b.id) && (a.name == b.name);
}

int main() {
    // 基础模板使用:自动类型推导
    int a = 10, b = 20;
    swapValue(a, b);
    cout << "交换后:a=" << a << ", b=" << b << endl;

    // 显式指定类型
    double c = 1.1, d = 2.2;
    swapValue<double>(c, d);
    cout << "交换后:c=" << c << ", d=" << d << endl;

    // 模板重载
    int x=1, y=2, z=3;
    swapValue(x, y, z);
    cout << "重载交换后:x=" << x << ", y=" << y << ", z=" << z << endl;

    // 模板具体化测试
    Person p1(1, "Tom"), p2(1, "Tom"), p3(2, "Jerry");
    cout << "p1与p2是否相等:" << boolalpha << MyCompare(p1, p2) << endl;
    cout << "p1与p3是否相等:" << boolalpha << MyCompare(p1, p3) << endl;

    return 0;
}

1.7.2 类模板

**概念:**类模板是通用的类定义,类的成员变量、成员函数的类型可通过模板参数指定,支持自定义数据类型的容器(如通用数组、通用 Person 类)。

关键特性:

  1. 无自动类型推导:必须显式指定模板参数类型(如Person<string, int>);
  2. 模板参数默认值:可给模板参数指定默认类型(如template <class T1, class T2=int>);
  3. 成员函数创建时机:类模板的成员函数在调用时才创建(普通类的成员函数在编译时创建);
  4. 类模板与继承:子类继承类模板时,需指定父类模板参数类型(或子类也声明为模板);
  5. 类模板与友元:友元函数可类内 / 类外实现,类外实现需提前声明模板;
  6. 类模板对象传参:支持 “指定类型传参”“参数模板化”“整个类模板化” 三种方式;
  7. 分文件编写:类模板成员函数类外实现时,需将声明和实现放在同一文件(或包含.cpp 文件),避免链接错误。

代码示例:

#include <iostream>
#include <string>
using namespace std;

// 通用数组类模板:支持内置类型/自定义类型,堆区存储,防浅拷贝
template <class T>
class MyArray {
private:
    T *pAddress;  // 堆区数组指针
    int capacity; // 数组容量
    int size;     // 实际元素个数
public:
    // 构造函数:初始化容量
    MyArray(int cap) : capacity(cap), size(0) {
        pAddress = new T[cap]; // 堆区开辟数组
    }

    // 拷贝构造:深拷贝防浅拷贝
    MyArray(const MyArray &arr) {
        capacity = arr.capacity;
        size = arr.size;
        pAddress = new T[arr.capacity]; // 重新开辟堆区空间
        for (int i = 0; i < size; i++) {
            pAddress[i] = arr.pAddress[i]; // 逐元素拷贝
        }
    }

    // 赋值运算符重载:深拷贝
    MyArray& operator=(const MyArray &arr) {
        if (pAddress != nullptr) { // 释放原有空间
            delete[] pAddress;
            pAddress = nullptr;
            capacity = 0;
            size = 0;
        }
        // 深拷贝新数据
        capacity = arr.capacity;
        size = arr.size;
        pAddress = new T[arr.capacity];
        for (int i = 0; i < size; i++) {
            pAddress[i] = arr.pAddress[i];
        }
        return *this;
    }

    // 尾插法
    void pushBack(const T &val) {
        if (size >= capacity) return; // 容量满则返回
        pAddress[size++] = val;
    }

    // 尾删法
    void popBack() {
        if (size <= 0) return; // 无元素则返回
        size--; // 逻辑删除(用户无法访问)
    }

    // 下标访问重载
    T& operator[](int index) {
        return pAddress[index]; // 支持arr[i]访问
    }

    // 获取容量/大小
    int getCapacity() { return capacity; }
    int getSize() { return size; }

    // 析构函数:释放堆区空间
    ~MyArray() {
        if (pAddress != nullptr) {
            delete[] pAddress;
            pAddress = nullptr;
        }
    }
};

// 测试内置类型
void testIntArray() {
    MyArray<int> arr(5);
    for (int i = 0; i < 5; i++) {
        arr.pushBack(i); // 尾插0-4
    }
    cout << "内置类型数组:";
    for (int i = 0; i < arr.getSize(); i++) {
        cout << arr[i] << " "; // 下标访问
    }
    cout << endl;

    arr.popBack(); // 尾删
    cout << "尾删后大小:" << arr.getSize() << endl;
}

// 测试自定义类型
class Person {
public:
    string name;
    int age;
    Person(string name, int age) : name(name), age(age) {}
};

void testPersonArray() {
    MyArray<Person> arr(10);
    arr.pushBack(Person("孙悟空", 999));
    arr.pushBack(Person("猪八戒", 888));

    cout << "自定义类型数组:" << endl;
    for (int i = 0; i < arr.getSize(); i++) {
        cout << "姓名:" << arr[i].name << ",年龄:" << arr[i].age << endl;
    }
}

int main() {
    testIntArray();
    testPersonArray();
    return 0;
}

1.8 容器

核心概念:STL(Standard Template Library,标准模板库)是 C++ 的核心组件,提供了通用的模板类和函数,实现了数据结构和算法的封装,核心分为六大组件

组件 作用
容器(Container) 存储数据的数据结构(如 vector、list、stack 等),分为序列式 / 关联式容器
算法(Algorithm) 操作容器数据的通用函数(如 sort、find、for_each 等),分为质变 / 非质变算法
迭代器(Iterator) 容器与算法的 “胶合剂”,提供统一的方式访问容器元素(类似指针)
仿函数(Functor) 行为类似函数的对象,可作为算法的策略(如自定义排序规则)
适配器(Adaptor) 修饰容器 / 迭代器 / 仿函数的接口(如 stack/deque 适配器)
空间配置器 管理容器的内存分配与释放

容器分类:

  1. 序列式容器:元素按插入顺序存储,有固定位置

    基础:vector(单端动态数组)、deque(双端数组)、list(双向循环链表)

    适配器:stack(栈,先进后出)、queue(队列,先进先出)

  2. 关联式容器:元素按 key 排序,无物理顺序(如 map / set / multimap / multiset)底层逻辑一般是红黑树

容器 键是否唯一 是否有序 核心特点
set 自动排序,无重复元素
multiset 自动排序,允许重复元素
map 键值对,键唯一,自动排序
multimap 键值对,键可重复,自动排序

迭代器分类:

迭代器类型 支持操作 适用容器
随机访问迭代器 []、at ()、± 整数、比较 vector、deque
双向迭代器 ++、– list、set、map
单向迭代器 ++ 输入 / 输出迭代器

关键:list/stack/queue不支持随机访问,无法直接用标准sort算法(list 内置 sort)

1.8.1 Vector容器(单端动态数组)

核心知识点:

  1. 动态扩展:容量不足时,重新分配更大内存,拷贝原数据,释放旧内存
  2. 支持随机访问([]/at ()),尾部插入 / 删除效率高,中间插入 / 删除效率低
  3. 常用接口:push_back/pop_backresize/reserveswapinsert/erase

代码示例:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 打印vector
void printVector(vector<int>& v) {
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << "  ";
    }
    cout << endl;
}

int main() {
    // 1. 创建与初始化
    vector<int> v1;                  // 默认构造
    vector<int> v2(5, 10);           // 5个10
    vector<int> v3(v2.begin(), v2.end()); // 区间构造

    // 2. 插入/删除
    v1.push_back(1); v1.push_back(2); v1.push_back(3);
    v1.insert(v1.begin() + 1, 99);   // 位置1插入99
    v1.pop_back();                   // 删除尾部元素

    // 3. 容量操作
    cout << "大小:" << v1.size() << endl;
    cout << "容量:" << v1.capacity() << endl;
    v1.reserve(100);                 // 预留空间(减少扩容次数)
    v1.resize(5, 0);                 // 调整大小,不足补0

    // 4. 访问元素(随机访问)
    cout << "第2个元素:" << v1[1] << endl;
    cout << "第3个元素:" << v1.at(2) << endl;
    cout << "首元素:" << v1.front() << endl;
    cout << "尾元素:" << v1.back() << endl;

    // 5. 排序(支持随机访问,可用标准sort)
    sort(v1.begin(), v1.end());
    printVector(v1);

    // 6. 互换容器(收缩内存)
    v1.resize(2);
    vector<int>(v1).swap(v1);        // 收缩容量至实际大小
    cout << "收缩后容量:" << v1.capacity() << endl;

    return 0;
}
1.8.2 deque(双端数组)

核心知识点:

  1. 双端插入 / 删除效率高,支持随机访问
  2. 底层为分段数组,通过中控器管理,避免 vector 的整体扩容
  3. 常用接口:push_front/pop_frontpush_back/pop_backsort

代码示例:

#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;

void printDeque(const deque<int>& d) {
    // const迭代器:防止修改容器
    for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
        cout << *it << "  ";
    }
    cout << endl;
}

int main() {
    deque<int> d;
    // 双端插入
    d.push_front(3); d.push_front(2); d.push_front(1);
    d.push_back(4); d.push_back(5); d.push_back(6);

    // 访问元素(随机访问)
    cout << "第3个元素:" << d[2] << endl;
    cout << "首元素:" << d.front() << endl;
    cout << "尾元素:" << d.back() << endl;

    // 排序
    sort(d.begin(), d.end());
    printDeque(d);

    // 删除
    d.pop_front(); // 删头部
    d.pop_back();  // 删尾部
    printDeque(d);

    return 0;
}
1.8.3 双向循环链表

核心知识点:

  1. 双向循环链表,每个节点含数据 + 前后指针,不支持随机访问
  2. 任意位置插入 / 删除效率高(仅修改指针),遍历效率低
  3. 内置算法:reverse(反转)、sort(排序,无随机访问,无法用标准 sort)
  4. 常用接口:push_front/pop_frontremove(删除指定值)、insert/erase

代码示例:

#include <iostream>
#include <list>
using namespace std;

void printList(list<int>& l) {
    for (list<int>::iterator it = l.begin(); it != l.end(); it++) {
        cout << *it << "  ";
    }
    cout << endl;
}

int main() {
    list<int> l;
    // 插入
    for (int i = 0; i < 5; i++) {
        l.push_back(rand() % 100); // 随机数
    }
    l.push_front(99);

    // 大小操作
    cout << "大小:" << l.size() << endl;
    l.resize(7, 100); // 不足补100

    // 反转+排序
    l.reverse(); // 反转
    printList(l);
    l.sort();    // 内置排序
    printList(l);

    // 删除
    l.remove(100); // 删除所有值为100的元素
    list<int>::iterator it = l.begin();
    l.erase(++it); // 删除指定位置

    // 访问(仅首尾)
    cout << "首元素:" << l.front() << endl;
    cout << "尾元素:" << l.back() << endl;

    return 0;
}
1.8.4 stack(栈,适配器)

核心知识点:

  1. 基于 deque 实现(适配器),先进后出(FILO)
  2. 无迭代器,不支持遍历,仅能访问栈顶
  3. 常用接口:push(入栈)、pop(出栈)、top(栈顶)、empty/size

代码示例:

#include <iostream>
#include <stack>
using namespace std;

int main() {
    stack<int> s;
    // 入栈
    for (int i = 0; i < 5; i++) {
        s.push(i);
    }

    // 访问栈顶
    cout << "栈顶:" << s.top() << endl;
    s.top() = 100; // 修改栈顶
    cout << "修改后栈顶:" << s.top() << endl;

    // 出栈
    s.pop();
    cout << "出栈后栈顶:" << s.top() << endl;

    // 状态
    cout << "是否为空:" << s.empty() << endl;
    cout << "元素个数:" << s.size() << endl;

    return 0;
}
1.8.5 queue(队列,适配器)

核心知识点:

  1. 基于 deque 实现,先进先出(FIFO)
  2. 无迭代器,仅能访问队头 / 队尾
  3. 常用接口:push(队尾入队)、pop(队头出队)、front(队头)、back(队尾)

代码示例:

#include <iostream>
#include <queue>
using namespace std;

int main() {
    queue<int> q;
    // 入队
    for (int i = 0; i < 5; i++) {
        q.push(i);
    }

    // 访问
    cout << "队头:" << q.front() << endl;
    cout << "队尾:" << q.back() << endl;

    // 修改队头
    q.front() = 12;
    cout << "修改后队头:" << q.front() << endl;

    // 出队
    q.pop();
    cout << "出队后队头:" << q.front() << endl;

    // 状态
    cout << "是否为空:" << q.empty() << endl;
    cout << "元素个数:" << q.size() << endl;

    return 0;
}
1.8.6 string(字符串容器)

核心知识点:

  1. 封装 char * 的类,管理内存(避免越界),支持丰富的字符串操作
  2. 常用接口:赋值(assign)、拼接(+=/append)、查找(find/rfind)、替换(replace)、子串(substr)

代码示例:

#include <iostream>
#include <string>
using namespace std;

int main() {
    // 1. 构造
    string s1 = "Hello";
    string s2(5, 'a');       // 5个a
    string s3(s1, 2, 3);     // 从索引2取3个字符:llo

    // 2. 拼接
    s1 += " World";
    s1.append("!");
    cout << s1 << endl; // Hello World!

    // 3. 查找与替换
    int pos = s1.find("World");
    if (pos != -1) {
        s1.replace(pos, 5, "C++"); // 替换World为C++
    }
    cout << s1 << endl; // Hello C++!

    // 4. 子串
    string email = "test@126.com";
    int at_pos = email.find('@');
    string domain = email.substr(at_pos); // 从@取到末尾:@126.com
    cout << "域名:" << domain << endl;

    // 5. 存取
    s1[0] = 'h'; // 修改第一个字符
    cout << s1.at(6) << endl; // 访问索引6:C

    return 0;
}
1.8.7 set / multiset / map / multimap

概念:

  1. 关联式容器以「键值对(pair)」或「单个值」为存储单元,底层通常是红黑树,支持快速查找、排序。

  2. 核心区别:

  3. 容器 键是否唯一 是否有序 核心特点
    set 自动排序,无重复元素
    multiset 自动排序,允许重复元素
    map 键值对,键唯一,自动排序
    multimap 键值对,键可重复,自动排序

代码示例:

#include <iostream>
#include <map>
#include <set>
#include <string>
using namespace std;

// 自定义排序规则(仿函数)
class MyMapCompare {
public:
    bool operator()(int v1, int v2) const {
        return v1 > v2; // 键降序排序
    }
};

int main() {
    // 1. set容器(唯一、有序)
    set<int> s;
    s.insert(3);
    s.insert(1);
    s.insert(3); // 重复元素,插入失败
    cout << "set容器元素:";
    for (int val : s) cout << val << " "; // 输出1 3
    cout << endl;

    // 2. map容器(键值对、键唯一)
    map<int, string, MyMapCompare> m;
    // 插入方式:pair / make_pair / value_type
    m.insert(pair<int, string>(1, "张三"));
    m.insert(make_pair(2, "李四"));
    m.insert(map<int, string>::value_type(3, "王五"));
    m[4] = "赵六"; // 不推荐:键不存在时会自动创建

    cout << "map降序遍历:" << endl;
    for (auto& p : m) {
        cout << "键:" << p.first << " 值:" << p.second << endl;
    }

    // 查找与统计
    auto pos = m.find(2);
    if (pos != m.end()) {
        cout << "找到键2:" << pos->second << endl; // 输出李四
    }
    cout << "键2的出现次数:" << m.count(2) << endl; // 输出1(map键唯一)

    return 0;
}
1.8.8 pair 对组

概念:

  1. pair 是 STL 的轻量级结构体,用于存储「两个关联数据」(键值对),包含两个公有成员:first(第一个值)、second(第二个值)。
  2. 核心用法:pair<T1,T2> p(a,b)make_pair(a,b)(更简洁)

代码示例:

#include <iostream>
#include <string>
#include <utility> // pair头文件(可省略,因其他容器头文件已包含)
using namespace std;

int main() {
    // 1. 创建pair
    pair<string, int> p1("张三", 20);
    auto p2 = make_pair("李四", 25); // 自动推导类型

    // 2. 访问成员
    cout << "p1:" << p1.first << " " << p1.second << endl; // 输出张三 20
    cout << "p2:" << p2.first << " " << p2.second << endl; // 输出李四 25

    // 3. 结合map使用
    pair<map<int,string>::iterator, bool> ret;
    map<int,string> m;
    ret = m.insert(pair<int,string>(1, "王五"));
    if (ret.second) {
        cout << "插入成功:" << ret.first->second << endl;
    }

    return 0;
}
1.8.9 仿函数

概念:

  1. 仿函数(Functor)是重载了 ()` 运算符的类 / 结构体,其对象可像普通函数一样调用,也叫 “函数对象”。
  2. 核心优势:可保存状态(成员变量)、可作为参数传递、支持自定义排序 / 筛选规则。

分类:

类型 说明 示例场景
一元谓词 返回 bool,接收 1 个参数 查找容器中 > 5 的元素
二元谓词 返回 bool,接收 2 个参数 自定义排序规则(降序)
普通仿函数 非 bool 返回,自定义逻辑 数值变换(val+200)
内建仿函数 STL 预定义() 算数运算(plus)、关系比较(greater)

代码示例:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // 内建仿函数头文件
using namespace std;

// 1. 普通仿函数(自定义加法)
class MyAdd {
public:
    int operator()(int a, int b) const {
        return a + b;
    }
};

// 2. 一元谓词(判断是否大于5)
class GreaterFive {
public:
    bool operator()(int val) const {
        return val > 5;
    }
};

// 3. 二元谓词(自定义降序排序)
class MyCompare {
public:
    bool operator()(int v1, int v2) const {
        return v1 > v2;
    }
};

// 4. 内建仿函数(STL预定义)
void testBuiltinFunctor() {
    // 算数仿函数:取反、加法
    negate<int> n; // 取反
    cout << "取反50:" << n(50) << endl; // 输出-50

    plus<int> p; // 加法
    cout << "12+23:" << p(12, 23) << endl; // 输出35

    // 关系仿函数:大于
    greater<int> g;
    cout << "10>5:" << boolalpha << g(10,5) << endl; // 输出true
}

int main() {
    // 普通仿函数调用
    MyAdd add;
    cout << "3+5:" << add(3,5) << endl; // 输出8

    // 一元谓词:查找>5的元素
    vector<int> v1{1,3,7,9,2};
    auto it = find_if(v1.begin(), v1.end(), GreaterFive());
    if (it != v1.end()) {
        cout << "找到第一个>5的元素:" << *it << endl; // 输出7
    }

    // 二元谓词:自定义排序
    vector<int> v2{1,5,3,9,2};
    sort(v2.begin(), v2.end(), MyCompare());
    cout << "降序排序结果:";
    for (int val : v2) cout << val << " "; // 输出9 5 3 2 1
    cout << endl;

    // 内建仿函数
    testBuiltinFunctor();

    return 0;
}

1.9 STL 常用算法

1.9.1 遍历算法(for_each/transform)

概念:

  1. for_each:遍历容器,执行自定义操作(函数 / 仿函数);
  2. transform:遍历源容器,将结果写入目标容器(支持数据变换)。

代码示例:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 遍历打印
void printVal(int val) {
    cout << val << " ";
}

// 数据变换仿函数
class TransformVal {
public:
    int operator()(int val) const {
        return val * 2; // 数值翻倍
    }
};

int main() {
    vector<int> v{1,2,3,4,5};
    // 1. for_each遍历
    cout << "for_each遍历:";
    for_each(v.begin(), v.end(), printVal); // 输出1 2 3 4 5
    cout << endl;

    // 2. transform变换
    vector<int> v2;
    v2.resize(v.size()); // 必须先扩容
    transform(v.begin(), v.end(), v2.begin(), TransformVal());
    cout << "transform变换后:";
    for (int val : v2) cout << val << " "; // 输出2 4 6 8 10
    cout << endl;

    return 0;
}
1.9.2 排序算法

概念:

  1. sort:对容器排序,默认升序,支持自定义仿函数;
  2. random_shuffle:随机打乱容器元素(需设置随机种子);
  3. merge:合并两个有序容器,结果存入第三个容器;
  4. reverse:反转容器元素顺序。

代码示例:

#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime> // 随机种子头文件
using namespace std;

// 降序排序仿函数
class Greater {
public:
    bool operator()(int a, int b) const {
        return a > b;
    }
};

int main() {
    srand((unsigned int)time(NULL)); // 设置随机种子

    // 1. sort排序
    vector<int> v1{3,1,4,2,5};
    sort(v1.begin(), v1.end()); // 升序
    cout << "升序排序:";
    for (int val : v1) cout << val << " "; // 输出1 2 3 4 5
    cout << endl;

    sort(v1.begin(), v1.end(), Greater()); // 降序
    cout << "降序排序:";
    for (int val : v1) cout << val << " "; // 输出5 4 3 2 1
    cout << endl;

    // 2. random_shuffle打乱
    random_shuffle(v1.begin(), v1.end());
    cout << "打乱后:";
    for (int val : v1) cout << val << " "; // 随机顺序
    cout << endl;

    // 3. merge合并(需两个有序容器)
    vector<int> v2{6,7,8};
    vector<int> v3;
    v3.resize(v1.size() + v2.size());
    merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
    cout << "合并后:";
    for (int val : v3) cout << val << " "; // 有序合并结果
    cout << endl;

    // 4. reverse反转
    reverse(v3.begin(), v3.end());
    cout << "反转后:";
    for (int val : v3) cout << val << " "; // 反转结果
    cout << endl;

    return 0;
}
1.9.3 查找算法

概念:

  1. find:查找指定元素,返回迭代器(自定义类型需重载==);
  2. find_if:按条件查找(一元谓词);
  3. binary_search:二分查找(仅支持有序容器),返回 bool;
  4. count:统计元素出现次数(自定义类型需重载==);
  5. count_if:按条件统计元素次数。

代码示例:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;

// 自定义类型
class Person {
public:
    string name;
    int age;
    Person(string n, int a) : name(n), age(a) {}

    // 重载==,支持find/count
    bool operator==(const Person& p) const {
        return name == p.name && age == p.age;
    }
};

// 查找条件:年龄>25
class AgeGreater25 {
public:
    bool operator()(const Person& p) const {
        return p.age > 25;
    }
};

int main() {
    // 1. 内置类型查找
    vector<int> v{1,2,3,4,5};
    auto it = find(v.begin(), v.end(), 3);
    if (it != v.end()) {
        cout << "找到元素:" << *it << endl; // 输出3
    }

    // 2. 自定义类型查找
    vector<Person> vp{Person("张三",20), Person("李四",30), Person("王五",25)};
    auto it2 = find_if(vp.begin(), vp.end(), AgeGreater25());
    if (it2 != vp.end()) {
        cout << "找到年龄>25的人:" << it2->name << endl; // 输出李四
    }

    // 3. 二分查找(有序容器)
    sort(v.begin(), v.end());
    bool exist = binary_search(v.begin(), v.end(), 3);
    cout << "3是否存在:" << boolalpha << exist << endl; // 输出true

    // 4. 统计次数
    int cnt = count(v.begin(), v.end(), 3);
    cout << "3出现次数:" << cnt << endl; // 输出1

    // 自定义类型统计
    int cnt_p = count(vp.begin(), vp.end(), Person("张三",20));
    cout << "张三出现次数:" << cnt_p << endl; // 输出1

    return 0;
}
1.9.4 集合算法

概念:求两个有序容器的「交集、并集、差集」,结果需存入第三个容器(推荐用back_inserter自动扩容)。

代码示例:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    vector<int> v1{1,2,3,4,5};
    vector<int> v2{3,4,5,6,7};
    vector<int> res;

    // 1. 交集(共同元素)
    set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), back_inserter(res));
    cout << "交集:";
    for (int val : res) cout << val << " "; // 输出3 4 5
    cout << endl;

    // 2. 并集(去重合并)
    res.clear();
    set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), back_inserter(res));
    cout << "并集:";
    for (int val : res) cout << val << " "; // 输出1 2 3 4 5 6 7
    cout << endl;

    // 3. 差集(v1有、v2无)
    res.clear();
    set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), back_inserter(res));
    cout << "差集(v1-v2):";
    for (int val : res) cout << val << " "; // 输出1 2
    cout << endl;

    return 0;
}
1.9.5 数值算法

概念:

  1. accumulate:计算容器元素累加和(<numeric>头文件);
  2. fill:将容器指定区间填充为指定值。

代码示例:

#include <iostream>
#include <vector>
#include <numeric> // accumulate/fill头文件
#include <algorithm>
using namespace std;

int main() {
    vector<int> v{1,2,3,4,5};

    // 1. accumulate累加(第三个参数是初始值)
    int sum = accumulate(v.begin(), v.end(), 0);
    cout << "累加和:" << sum << endl; // 输出15

    // 2. fill填充
    fill(v.begin(), v.end(), 10);
    cout << "填充后:";
    for (int val : v) cout << val << " "; // 输出10 10 10 10 10
    cout << endl;

    return 0;
}

更多推荐