C++知识点梳理
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修饰指针:
const int *p:值不能改,指向可以改int *const p:指向不能改,值可以改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 结构体
概念:结构体是自定义数据类型,可封装多个不同类型的数据;支持嵌套、数组、指针、函数参数传递
注意点:
-
结构体传参:
-
值传递:形参是结构体副本,修改形参不影响实参;
-
地址传递:形参是结构体指针,修改形参影响实参(加
const可禁止修改);
-
-
结构体指针访问成员:
->(替代.); -
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 回收 |
注意点:
- 栈区不能返回局部变量的地址(局部变量出函数栈帧即销毁,地址变为野地址);
- 堆区数据需手动管理,避免内存泄漏;
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销毁
}
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 关键字
概念:
- new:用于在堆区手动开辟内存,返回对应数据类型的指针;
- delete:释放
new开辟的单个数据内存; delete[]:释放new[]开辟的数组内存(必须配套使用,否则内存泄漏)。
注意点:
new int(10):开辟单个 int 内存,初始值为 10;new int[10]:开辟长度为 10 的 int 数组内存;- 释放后的内存不可再次访问(非法内存访问)。
代码示例:
#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 引用
概念:引用是变量的别名,本质是 “指针常量”(指向不可改,值可改),语法:数据类型 &别名 = 原变量;
关键点:
- 引用必须初始化(
int &a;错误); - 初始化后不可更改指向(只能绑定一个原变量);
- 引用做函数参数:可直接修改实参(替代指针,简化操作);
- 引用做返回值:不可返回局部变量的引用(栈区数据销毁),可返回静态变量 / 堆区数据的引用;
- 常量引用:
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 函数进阶
**函数默认参数:**为函数参数指定默认值,调用时若不传该参数则使用默认值,传参则覆盖默认值。
- 若某个参数指定默认值,其后所有参数都必须有默认值;
- 函数声明和实现不能同时指定默认值(二选一)。
代码示例:
#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;
}
**函数重载:**同一作用域下,函数名相同,参数类型 / 个数 / 顺序不同,可实现重载(提高复用性)。
- 返回值不能作为重载条件;
- 引用可作为重载条件(
int &avsconst int &a); - 重载避免搭配默认参数(可能导致二义性)。
代码示例:
#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 类与对象:
- 类:自定义数据类型,封装了属性(成员变量)和行为(成员函数 / 方法),是对象的模板。
- 对象:类的实例化,占用实际内存空间,可调用类的属性和方法。
- 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),可重载(有参 / 无参 / 拷贝) |
对象创建时自动调用,仅一次 |
| 析构函数 | ~类名(){},无参数、无返回值,不可重载 |
对象销毁时自动调用,仅一次 |
构造函数分类:
- 无参构造:默认构造(不写时编译器自动生成);
- 有参构造:初始化对象属性;
- 拷贝构造:
类名(const 类名 &obj),用已有对象初始化新对象,解决浅拷贝问题。
初始化列表:
- 语法:
构造函数(参数列表) : 属性1(值1), 属性2(值2)... {}; - 作用:直接初始化成员属性(尤其适合 const 属性、类类型成员)。
类对象作为类成员:
- 构造顺序:先构造成员对象,再构造自身;
- 析构顺序:先析构自身,再析构成员对象。
代码示例:
#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 浅拷贝与深拷贝
概念:
- 浅拷贝:编译器默认的拷贝方式,仅简单赋值(指针指向同一内存地址,易导致重复释放);
- 深拷贝:手动在堆区重新开辟内存,拷贝数据(解决浅拷贝的内存泄漏 / 重复释放问题)。
代码示例:
#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 面向对象的三大特性(封装/继承/多态)
概念:封装
- 概念:将属性和行为封装到类中,通过访问权限控制读写,提高代码安全性;
- 优势:可控制属性读写权限、检测输入有效性。
代码示例:
#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;
}
概念:继承
**定义:**面向对象三大特性之一,允许子类(派生类)复用父类(基类)的属性和方法,减少代码冗余。
- 语法:
class 子类 : 继承权限 父类 {}; - 继承权限:
public(父类权限不变)、private(父类所有权限变为 private)、protected(父类 public 变为 protected)。
关键特性:
- 父类非静态成员都会被继承(编译器隐藏不可直接访问,但确实存在);
- 构造 / 析构顺序:父类构造 → 子类构造 → 子类析构 → 父类析构;
- 同名成员:子类优先访问自身,加
父类::可访问父类同名成员; - 多继承:
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;
}
概念:多态
**分类: ** 静态多态:编译期确定(函数重载、运算符重载); 动态多态:编译期确定(函数重载、运算符重载);
动态多态条件: 有继承关系; 子类重写父类虚函数(返回值、函数名、参数完全一致)。
动态多态使用:父类指针 / 引用指向子类对象。
纯虚函数 / 抽象类:
- 纯虚函数:
virtual 返回值 函数名() = 0; - 抽象类:包含纯虚函数的类,无法实例化,子类必须重写纯虚函数(否则也为抽象类)。
代码示例:
#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 静态成员
概念:静态成员分为静态成员变量和静态成员函数,核心是 “类级共享”,不属于单个对象。
静态成员变量:
- 所有对象共享同一份数据;
- 编译阶段分配内存(全局区);
- 类内声明,类外初始化(
数据类型 类名::变量名 = 初始值); - 受访问权限(public/private)限制。
静态成员函数:
- 所有对象共享同一个函数;
- 只能访问静态成员变量(无 this 指针,无法访问非静态成员);
- 可通过
对象名.函数名或类名::函数名调用。
代码示例:
#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字节内存(编译器分配唯一地址,区分不同空对象); - 静态成员变量不占用对象内存(属于类,全局共享)。
概念: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 常函数与常对象
概念:
-
常函数:成员函数后加
const修饰(返回值 函数名() const),本质是修饰this指针(const 类名* const this),限制函数内不能修改成员变量; 例外:
mutable修饰的成员变量,可在常函数中修改。 -
常对象:对象前加
const(const 类名 对象名),常对象只能调用常函数,且不能修改成员变量(除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),打破类的封装性,适用于特殊场景(如跨类数据访问)。
友元的三种实现形式:
- 全局函数做友元:在类内用
friend声明全局函数; - 类做友元:在类内用
friend class 类名声明友元类; - 成员函数做友元:在类内用
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++ 泛型编程的核心,允许编写与类型无关的代码,大幅提高代码复用性。分为函数模板和类模板两类
- 泛型编程:编写不依赖具体数据类型的代码,仅用 “虚拟类型”(模板参数)指代类型,编译时根据实际传入类型生成具体代码。
- 模板参数:用
template <class T>(或template <typename T>)声明,T为 “类型形参”,可理解为类型的 “占位符”。 - 模板实例化:使用模板时,编译器根据传入的具体类型,生成对应类型的函数 / 类(分为隐式实例化、显式实例化)
1.7.1 函数模板
**概念:**函数模板是通用的函数定义,参数类型、返回值类型可通过模板参数指定,支持不同类型的参数复用同一套逻辑。
关键特性:
- 自动类型推导:调用时编译器根据实参推导模板参数类型(需推导结果唯一);
- 显式指定类型:调用时通过
<类型>强制指定模板参数类型; - 与普通函数的区别:自动类型推导不支持隐式类型转换,显式指定类型支持;
- 模板重载:可定义参数个数 / 类型不同的同名函数模板;
- 模板具体化:针对特定类型(如自定义类)编写专属实现,优先级高于通用模板。
代码示例:
#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 类)。
关键特性:
- 无自动类型推导:必须显式指定模板参数类型(如
Person<string, int>); - 模板参数默认值:可给模板参数指定默认类型(如
template <class T1, class T2=int>); - 成员函数创建时机:类模板的成员函数在调用时才创建(普通类的成员函数在编译时创建);
- 类模板与继承:子类继承类模板时,需指定父类模板参数类型(或子类也声明为模板);
- 类模板与友元:友元函数可类内 / 类外实现,类外实现需提前声明模板;
- 类模板对象传参:支持 “指定类型传参”“参数模板化”“整个类模板化” 三种方式;
- 分文件编写:类模板成员函数类外实现时,需将声明和实现放在同一文件(或包含.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 适配器) |
| 空间配置器 | 管理容器的内存分配与释放 |
容器分类:
-
序列式容器:元素按插入顺序存储,有固定位置
基础:
vector(单端动态数组)、deque(双端数组)、list(双向循环链表) 适配器:
stack(栈,先进后出)、queue(队列,先进先出) -
关联式容器:元素按 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容器(单端动态数组)
核心知识点:
- 动态扩展:容量不足时,重新分配更大内存,拷贝原数据,释放旧内存
- 支持随机访问([]/at ()),尾部插入 / 删除效率高,中间插入 / 删除效率低
- 常用接口:
push_back/pop_back、resize/reserve、swap、insert/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(双端数组)
核心知识点:
- 双端插入 / 删除效率高,支持随机访问
- 底层为分段数组,通过中控器管理,避免 vector 的整体扩容
- 常用接口:
push_front/pop_front、push_back/pop_back、sort
代码示例:
#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 双向循环链表
核心知识点:
- 双向循环链表,每个节点含数据 + 前后指针,不支持随机访问
- 任意位置插入 / 删除效率高(仅修改指针),遍历效率低
- 内置算法:
reverse(反转)、sort(排序,无随机访问,无法用标准 sort) - 常用接口:
push_front/pop_front、remove(删除指定值)、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(栈,适配器)
核心知识点:
- 基于 deque 实现(适配器),先进后出(FILO)
- 无迭代器,不支持遍历,仅能访问栈顶
- 常用接口:
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(队列,适配器)
核心知识点:
- 基于 deque 实现,先进先出(FIFO)
- 无迭代器,仅能访问队头 / 队尾
- 常用接口:
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(字符串容器)
核心知识点:
- 封装 char * 的类,管理内存(避免越界),支持丰富的字符串操作
- 常用接口:赋值(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
概念:
-
关联式容器以「键值对(pair)」或「单个值」为存储单元,底层通常是红黑树,支持快速查找、排序。
-
核心区别:
-
容器 键是否唯一 是否有序 核心特点 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 对组
概念:
- pair 是 STL 的轻量级结构体,用于存储「两个关联数据」(键值对),包含两个公有成员:
first(第一个值)、second(第二个值)。 - 核心用法:
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 仿函数
概念:
- 仿函数(Functor)是重载了 ()` 运算符的类 / 结构体,其对象可像普通函数一样调用,也叫 “函数对象”。
- 核心优势:可保存状态(成员变量)、可作为参数传递、支持自定义排序 / 筛选规则。
分类:
| 类型 | 说明 | 示例场景 |
|---|---|---|
| 一元谓词 | 返回 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)
概念:
for_each:遍历容器,执行自定义操作(函数 / 仿函数);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 排序算法
概念:
sort:对容器排序,默认升序,支持自定义仿函数;random_shuffle:随机打乱容器元素(需设置随机种子);merge:合并两个有序容器,结果存入第三个容器;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 查找算法
概念:
find:查找指定元素,返回迭代器(自定义类型需重载==);find_if:按条件查找(一元谓词);binary_search:二分查找(仅支持有序容器),返回 bool;count:统计元素出现次数(自定义类型需重载==);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 数值算法
概念:
accumulate:计算容器元素累加和(<numeric>头文件);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;
}
更多推荐


所有评论(0)