1. 函数模板与普通函数的调用机制
函数模板和普通函数同时存在时,C++ 编译器的调用规则遵循 “优先普通函数 → 模板精准匹配 → 模板重载” 的逻辑,确保调用符合开发者意图。
一、C++ 编译器优先考虑普通函数
原理:
普通函数是开发者显式定义的 “定制逻辑”,编译器默认认为这是更贴合需求的实现,因此优先匹配普通函数,即便函数模板能生成完全匹配的实例。
示例验证:
#include <iostream>
using namespace std;
// 普通函数
void show(int a) {
cout << "普通函数:" << a << endl;
}
// 函数模板
template <typename T>
void show(T a) {
cout << "函数模板:" << a << endl;
}
int main() {
int num = 10;
// 优先调用普通函数(输出:普通函数:10)
show(num);
return 0;
}
关键:
若普通函数和模板函数都能匹配,编译器无条件优先选择普通函数。若想强制调用模板函数,需用空模板实参列表。
二、空模板实参列表:限定仅模板匹配
语法:
调用时显式添加 <>
,如 show<>(num)
,告诉编译器 “跳过普通函数,仅从模板中匹配”。
示例验证:
int main() {
int num = 10;
// 强制调用函数模板(输出:函数模板:10)
show<>(num);
return 0;
}
适用场景:
当普通函数的逻辑不符合需求,且模板函数能提供更通用或更优的实现时,用空模板实参列表强制调用模板。
三、函数模板的重载
规则:
函数模板的重载逻辑与普通函数一致,通过 ** 参数个数、类型(模板参数 + 函数参数结合)** 区分不同版本。
示例验证:
template <typename T>
void print(T a) {
cout << "模板1:" << a << endl;
}
template <typename T, typename U>
void print(T a, U b) {
cout << "模板2:" << a << ", " << b << endl;
}
int main() {
// 调用模板1(输出:模板1:5)
print(5);
// 调用模板2(输出:模板2:3, abc)
print(3, "abc");
return 0;
}
关键:
模板重载需保证函数签名(参数个数、类型)不同,否则会引发编译歧义。
四、模板的 “更好匹配” 优先
场景:
普通函数需要隐式类型转换,而模板函数能精准匹配(无需转换)时,编译器优先选择模板函数。
示例验证:
void display(double a) {
cout << "普通函数:" << a << endl;
}
template <typename T>
void display(T a) {
cout << "函数模板:" << a << endl;
}
int main() {
int val = 5;
// 普通函数需要 int→double 转换;模板直接 T=int 匹配,更优(输出:函数模板:5)
display(val);
return 0;
}
原理:
隐式类型转换会增加运行时开销(或潜在风险),模板的精准匹配更符合 “最小转换” 原则,因此编译器优先选择模板。
2. 函数模板推导类型为自定义类型的解决:运算符重载
当函数模板推导的类型是自定义类(如 MyClass
)时,若模板中使用了运算符(如 +
、<<
),需为自定义类重载相应的运算符,否则编译器无法识别运算符,引发编译错误。
问题场景(未重载运算符)
class MyClass {
public:
int val;
MyClass(int v) : val(v) {}
// 未重载 + 运算符
};
template <typename T>
T add(T a, T b) {
// 编译报错:MyClass 未定义 + 运算符
return a + b;
}
int main() {
MyClass a(5), b(3);
// 调用 add<MyClass>,触发编译错误
MyClass result = add(a, b);
return 0;
}
解决方案:重载运算符
为 MyClass
重载 +
运算符,告诉编译器 “如何将两个 MyClass
对象相加”。
修复后的代码:
class MyClass {
public:
int val;
MyClass(int v) : val(v) {}
// 重载 + 运算符:定义 MyClass 对象的相加逻辑
MyClass operator+(const MyClass& other) const {
// 返回新对象,val 为两者之和
return MyClass(val + other.val);
}
};
template <typename T>
T add(T a, T b) {
// 调用重载的 + 运算符
return a + b;
}
int main() {
MyClass a(5), b(3);
MyClass result = add(a, b);
// 输出:8(5 + 3 = 8)
cout << "结果:" << result.val << endl;
return 0;
}
扩展:重载其他运算符(如 <<
)
若模板中使用 cout << a
,需为 MyClass
重载 <<
运算符:
// 全局重载 << 运算符(需访问私有成员时,可声明为友元)
ostream& operator<<(ostream& os, const MyClass& obj) {
os << obj.val;
return os;
}
template <typename T>
void print(T a) {
// 调用重载的 << 运算符
cout << a << endl;
}
int main() {
MyClass a(5);
// 输出:5
print(a);
return 0;
}
3. C++ 异常机制 vs C 语言错误处理
C++ 异常机制通过类 / 对象传递错误,相比 C 语言的 “返回值 + errno
” 方式,在错误处理的强制性、语义清晰度、上下文传递等方面有显著优势。
对比维度:C 语言 vs C++ 异常
特性 | C 语言(返回值 / errno ) |
C++ 异常机制 |
---|---|---|
错误是否可忽略 | 调用者可能忘记检查返回值,错误被无声忽略 | 异常必须捕获(否则程序终止),强迫处理错误 |
语义清晰度 | 整型返回值无明确含义,调用者需记忆约定 | 异常用类 / 对象区分,类名即错误类型(如 FileNotFoundException ) |
上下文信息 | 仅返回一个错误码,无额外上下文 | 异常对象可携带详细信息(文件名、错误码、堆栈跟踪等) |
错误传递效率 | 必须逐层返回错误码,中间函数冗余 | 支持跳级捕获(跳过无需处理的中间函数),代码更简洁 |
示例:C 语言的错误处理(低效且易错)
#include <stdio.h>
#include <stdlib.h>
// 返回 0 成功,-1 失败
int openFile(const char* filename) {
// 模拟打开失败
return -1;
}
void process() {
int result = openFile("test.txt");
// 调用者可能忘记检查返回值,错误被忽略
if (result == -1) {
fprintf(stderr, "文件打开失败\n");
exit(1);
}
}
int main() {
process();
return 0;
}
示例:C++ 异常机制(强制处理 + 丰富上下文)
#include <iostream>
#include <string>
using namespace std;
class FileOpenException {
public:
string filename;
int errorCode;
FileOpenException(const string& fn, int ec) : filename(fn), errorCode(ec) {}
};
void openFile(const char* filename) {
// 模拟打开失败,抛出异常对象
throw FileOpenException(filename, -1);
}
void process() {
try {
openFile("test.txt");
} catch (const FileOpenException& e) {
// 捕获异常,打印详细上下文
cerr << "文件打开失败:" << e.filename
<< ",错误码:" << e.errorCode << endl;
// 可选择恢复或终止程序
throw; // 重新抛出,让上层处理
}
}
int main() {
try {
process();
} catch (const FileOpenException& e) {
cerr << "上层捕获:" << e.filename << " 打开失败" << endl;
}
return 0;
}
4. 四种类型转换的作用与区别
C++ 提供四种显式类型转换:static_cast
、dynamic_cast
、const_cast
、reinterpret_cast
。它们的适用场景、安全性和底层逻辑差异显著。
一、静态转换(static_cast
)
作用:
用于类似类型间的安全转换(如 int ↔ float
)、类层次的向上 / 向下转换(需谨慎)、消除指针 / 函数指针的歧义(如 void* → T*
)。
示例:
// 基本类型转换(安全)
int a = 10;
float b = static_cast<float>(a);
// 类层次向上转换(安全:派生类 → 基类)
class Base {};
class Derived : public Base {};
Derived d;
Base* b_ptr = static_cast<Base*>(&d);
// 类层次向下转换(危险:基类 → 派生类,需确保实际类型)
Base base_obj;
// 危险!若 base_obj 实际不是 Derived,访问派生类成员会崩溃
Derived* d_ptr = static_cast<Derived*>(&base_obj);
特点:
- 编译时检查:语法错误(如完全无关类型转换)会报错,但逻辑错误(如不安全的向下转换)无法在编译时发现。
- 无运行时开销:仅调整类型标识,运行时无额外检查。
二、动态转换(dynamic_cast
)
作用:
专门用于类层次的向下转换(基类指针 / 引用 → 派生类指针 / 引用 ),依赖运行时类型信息(RTTI),确保转换的安全性。
示例:
class Base { virtual void func() {} }; // 必须有虚函数(启用 RTTI)
class Derived : public Base {};
int main() {
Base* base_ptr = new Derived();
// 安全向下转换:实际类型是 Derived,转换成功
Derived* d_ptr = dynamic_cast<Derived*>(base_ptr);
if (d_ptr) {
cout << "转换成功" << endl;
}
Base* wrong_ptr = new Base();
// 转换失败:返回 nullptr
Derived* d_wrong = dynamic_cast<Derived*>(wrong_ptr);
if (!d_wrong) {
cout << "转换失败" << endl;
}
return 0;
}
特点:
- 运行时检查:依赖 RTTI(需类包含虚函数),转换失败时返回
nullptr
(指针)或抛出std::bad_cast
(引用)。 - 安全向下转换:仅当对象实际类型匹配时成功,避免非法访问派生类成员。
三、常量转换(const_cast
)
作用:
修改类型的 const
/ volatile
属性,仅能用于指针或引用(不能直接转换非指针的 const
变量)。
示例:
void modify(const int& num) {
// 移除 const,允许修改(仅当 num 实际是非 const 变量时安全)
int& non_const_num = const_cast<int&>(num);
non_const_num = 20;
}
int main() {
int value = 10;
modify(value); // value 变为 20(安全)
cout << "value: " << value << endl;
const int const_val = 5;
// 危险!const_val 本身是 const,修改会触发未定义行为
int& risky = const_cast<int&>(const_val);
risky = 10; // 可能崩溃或值不变
return 0;
}
特点:
- 谨慎使用:若原始对象是
const
,移除const
后修改会导致未定义行为(程序可能崩溃)。 - 仅操作指针 / 引用:不能直接转换非指针 / 引用的
const
变量(如const int a = 5; int b = const_cast<int>(a);
非法 )。
四、重新解释转换(reinterpret_cast
)
作用:
底层二进制重新解释,将一种类型的指针 / 引用 / 整数强制视为另一种类型,不做任何类型检查,仅用于极特殊场景(如硬件交互、二进制兼容)。
示例:
int value = 65;
// 将 int* 转成 char*,重新解释二进制:65 → 'A'(ASCII 码)
char* char_ptr = reinterpret_cast<char*>(&value);
cout << *char_ptr << endl; // 输出 'A'
// 危险操作:无意义类型转换
double* d_ptr = reinterpret_cast<double*>(&value);
// *d_ptr 取值无意义(int 和 double 二进制布局不同)
cout << *d_ptr << endl;
特点:
- 完全不检查类型:编译器不验证转换合法性,行为完全由开发者保证。
- 高风险:若类型二进制布局不兼容,转换后访问数据会导致未定义行为(程序崩溃、数据错乱)。
5. STL 常用集合算法(set_intersection
、set_union
等)
STL 提供一系列针对有序集合(如 set
、vector
排序后)的算法,用于计算交集、并集、差集,简化集合操作。
示例:集合算法的使用
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
using namespace std;
// 打印容器内容的函数
template <typename T>
void printContainer(const T& container) {
for (const auto& elem : container) {
cout << elem << " ";
}
cout << endl;
}
int main() {
set<int> setA = {1, 3, 5, 7, 9};
set<int> setB = {3, 5, 8, 9, 10};
// 结果存储容器
vector<int> resultIntersection; // 交集
vector<int> resultUnion; // 并集
vector<int> resultDifference; // 差集(A - B)
// 1. 计算交集(A ∩ B)
set_intersection(
setA.begin(), setA.end(),
setB.begin(), setB.end(),
back_inserter(resultIntersection)
);
cout << "交集 (A ∩ B):";
printContainer(resultIntersection); // 输出:3 5 9
// 2. 计算并集(A ∪ B)
set_union(
setA.begin(), setA.end(),
setB.begin(), setB.end(),
back_inserter(resultUnion)
);
cout << "并集 (A ∪ B):";
printContainer(resultUnion); // 输出:1 3 5 7 8 9 10
// 3. 计算差集(A - B)
set_difference(
setA.begin(), setA.end(),
setB.begin(), setB.end(),
back_inserter(resultDifference)
);
cout << "差集 (A - B):";
printContainer(resultDifference); // 输出:1 7
return 0;
}
关键细节
- 集合需有序:
set_intersection
、set_union
、set_difference
要求输入的集合是有序的(如set
本身有序,vector
需先sort
)。 - 结果存储:需用
back_inserter
将结果插入到空容器(如vector
),避免内存越界。
6. 常用的算法生成算法
生成算法用于初始化容器或计算元素累计值,通过简洁的接口替代手动遍历,提升代码效率。
1. accumulate
算法(累加 / 累计)
核心功能:
计算容器中指定区间元素的累计值(默认是求和,也可通过自定义函数实现其他累计逻辑,如乘积、字符串拼接)。
用法细节:
- 需包含头文件
<numeric>
。 - 语法:
accumulate(起始迭代器, 结束迭代器, 初始值, [自定义操作函数])
。- 初始值:累计的起点(决定返回值类型)。
- 自定义操作函数(可选):默认是
+
运算,可替换为其他二元操作(如*
)。
示例 1:基本求和
#include <vector>
#include <numeric>
#include <iostream>
using namespace std;
int main() {
vector<int> v = {1, 2, 3, 4};
// 从 0 开始累加:0 + 1 + 2 + 3 + 4 = 10
int sum = accumulate(v.begin(), v.end(), 0);
cout << "总和:" << sum << endl; // 输出 10
return 0;
}
示例 2:自定义累计(乘积)
#include <vector>
#include <numeric>
#include <iostream>
using namespace std;
// 自定义操作:计算乘积
int multiply(int acc, int val) {
return acc * val;
}
int main() {
vector<int> v = {1, 2, 3};
// 从 1 开始累乘:1 * 1 * 2 * 3 = 6
int product = accumulate(v.begin(), v.end(), 1, multiply);
cout << "乘积:" << product << endl; // 输出 6
return 0;
}
2. fill
算法(批量填充)
核心功能:
将容器中指定区间的所有元素设置为同一个值,实现批量初始化或覆盖。
用法细节:
- 需包含头文件
<algorithm>
。 - 语法:
fill(起始迭代器, 结束迭代器, 目标值)
。 - 注意:容器必须已分配足够内存(如
vector
需提前resize
或初始化时指定大小)。
示例:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
// 初始化一个包含 5 个元素的 vector(默认值为 0)
vector<int> v(5);
// 将所有元素填充为 3
fill(v.begin(), v.end(), 3);
// 输出:3 3 3 3 3
for (int num : v) {
cout << num << " ";
}
return 0;
}
扩展:fill_n
算法(填充指定个数元素)
fill_n(起始位置, 元素个数, 目标值)
:从起始位置开始,填充指定个数的元素为目标值(需确保容器有足够空间)。
vector<int> v(10);
// 从 v.begin() 开始,填充前 3 个元素为 5
fill_n(v.begin(), 3, 5);
// v 变为:5 5 5 0 0 0 0 0 0 0
7. 常用的拷贝和替换算法
这类算法用于元素拷贝、值替换、容器交换,简化数据迁移和修改操作。
1. copy
算法(元素拷贝)
核心功能:
将一个容器中指定区间的元素拷贝到另一个容器的指定位置(覆盖目标容器的元素)。
用法细节:
- 需包含头文件
<algorithm>
。 - 语法:
copy(源起始迭代器, 源结束迭代器, 目标起始迭代器)
。 - 注意:目标容器必须有足够空间(可通过
resize
或back_inserter
动态扩展)。
示例:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
vector<int> v1 = {1, 2, 3};
vector<int> v2(3); // 目标容器需提前分配空间
// 将 v1 的元素拷贝到 v2
copy(v1.begin(), v1.end(), v2.begin());
// 输出 v2:1 2 3
for (int num : v2) {
cout << num << " ";
}
return 0;
}
动态扩展目标容器:
用 back_inserter
自动在目标容器尾部插入元素(无需提前分配空间):
vector<int> v1 = {1, 2, 3};
vector<int> v2;
// 动态插入到 v2 尾部
copy(v1.begin(), v1.end(), back_inserter(v2));
2. replace
算法(值替换)
核心功能:
将容器中指定区间内所有等于 “旧值” 的元素替换为 “新值”。
用法细节:
- 语法:
replace(起始迭代器, 结束迭代器, 旧值, 新值)
。
示例:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
vector<int> v = {1, 2, 2, 3, 2};
// 将所有 2 替换为 5
replace(v.begin(), v.end(), 2, 5);
// 输出:1 5 5 3 5
for (int num : v) {
cout << num << " ";
}
return 0;
}
3. replace_if
算法(条件替换)
核心功能:
根据自定义条件(如 “大于 10”“偶数”),将容器中符合条件的元素替换为指定值。
用法细节:
- 语法:
replace_if(起始迭代器, 结束迭代器, 条件函数, 新值)
。 - 条件函数:返回
bool
的一元函数(可通过 lambda 表达式简化)。
示例:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
vector<int> v = {1, 3, 5, 7, 9, 10};
// 将所有偶数(条件)替换为 0
replace_if(v.begin(), v.end(), [](int x) {
return x % 2 == 0;
}, 0);
// 输出:1 3 5 7 9 0
for (int num : v) {
cout << num << " ";
}
return 0;
}
4. swap
算法(容器交换)
核心功能:
交换两个同类型容器的所有元素(底层仅交换指针,效率极高,无需拷贝元素)。
用法细节:
- 语法:
swap(容器1, 容器2)
。 - 适用于所有 STL 容器(
vector
、list
、map
等)。
示例:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
vector<int> v1 = {1, 2};
vector<int> v2 = {3, 4, 5};
// 交换 v1 和 v2 的内容
swap(v1, v2);
// 输出 v1:3 4 5;v2:1 2
for (int num : v1) cout << num << " ";
cout << endl;
for (int num : v2) cout << num << " ";
return 0;
}
8. 常见的查找算法
查找算法用于在容器中定位元素,支持精确匹配、条件匹配、相邻重复检测等场景。
1. find
算法(精确查找)
核心功能:
在容器中查找等于 “目标值” 的第一个元素,返回其迭代器(未找到则返回 end()
)。
用法细节
- 语法:
find(起始迭代器, 结束迭代器, 目标值)
。 - 自定义类型需重载
==
运算符(否则无法比较)。
示例:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
vector<int> v = {1, 2, 3, 4};
// 查找值为 3 的元素
auto it = find(v.begin(), v.end(), 3);
if (it != v.end()) {
cout << "找到元素:" << *it << ",位置:" << it - v.begin() << endl;
} else {
cout << "未找到元素" << endl;
}
return 0;
}
2. find_if
算法(条件查找)
核心功能:
查找容器中第一个满足自定义条件的元素,返回其迭代器(未找到则返回 end()
)。
用法细节
- 语法:
find_if(起始迭代器, 结束迭代器, 条件函数)
。 - 条件函数:返回
bool
的一元函数(如 lambda 表达式)。
示例:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
vector<int> v = {1, 3, 5, 7, 9};
// 查找第一个大于 5 的元素
auto it = find_if(v.begin(), v.end(), [](int x) {
return x > 5;
});
if (it != v.end()) {
cout << "找到元素:" << *it << endl; // 输出 7
}
return 0;
}
3. adjacent_find
算法(相邻重复查找)
核心功能:
查找容器中第一对相邻且相等的元素,返回第一个元素的迭代器(未找到则返回 end()
)。
用法细节:
- 语法:
adjacent_find(起始迭代器, 结束迭代器)
。
示例:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
vector<int> v = {1, 2, 2, 3, 4, 4, 4};
// 查找第一对相邻重复元素
auto it = adjacent_find(v.begin(), v.end());
if (it != v.end()) {
cout << "找到相邻重复元素:" << *it << " 和 " << *(it + 1) << endl; // 输出 2 和 2
}
return 0;
}
4. binary_search
算法(二分查找)
核心功能:
在有序容器中快速判断 “目标值” 是否存在,返回 bool
结果(存在为 true
,否则为 false
)。
用法细节:
- 前提:容器必须是升序排列(若为降序,需传入自定义比较函数)。
- 语法:
binary_search(起始迭代器, 结束迭代器, 目标值)
。 - 效率:时间复杂度为
O(log n)
,远高于线性查找。
示例:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
vector<int> v = {1, 3, 5, 7, 9};
// 二分查找前必须确保容器有序(已排序)
bool exists = binary_search(v.begin(), v.end(), 5);
cout << "5 是否存在:" << (exists ? "是" : "否") << endl; // 输出 是
exists = binary_search(v.begin(), v.end(), 4);
cout << "4 是否存在:" << (exists ? "是" : "否") << endl; // 输出 否
return 0;
}
9. 常用的遍历算法
遍历算法用于访问容器中所有元素,支持读取、修改或转换元素,替代手动 for
循环,代码更简洁。
1. for_each
算法(遍历执行操作)
核心功能:
对容器中指定区间的每个元素执行自定义操作(如打印、修改值)。
用法细节:
- 语法:
for_each(起始迭代器, 结束迭代器, 操作函数)
。 - 操作函数:可是普通函数、lambda 表达式或仿函数,无返回值(或返回值被忽略)。
示例:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
// 自定义操作:打印元素
void print(int x) {
cout << x << " ";
}
int main() {
vector<int> v = {1, 2, 3};
// 遍历并打印所有元素
for_each(v.begin(), v.end(), print);
cout << endl;
// 用 lambda 表达式修改元素(每个元素乘 2)
for_each(v.begin(), v.end(), [](int& x) {
x *= 2;
});
// 输出修改后的值:2 4 6
for_each(v.begin(), v.end(), print);
return 0;
}
2. transform
算法(转换并搬运元素)
核心功能:
对一个容器中的元素执行转换操作(如乘 2、字符串拼接),并将结果搬运到另一个容器。
用法细节:
- 语法:
transform(源起始迭代器, 源结束迭代器, 目标起始迭代器, 转换函数)
。 - 转换函数:接收源元素类型,返回目标元素类型(如
int → int
、string → int
)。
示例:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
vector<int> v1 = {1, 2, 3};
vector<int> v2(3); // 目标容器需提前分配空间
// 将 v1 元素乘 2 后存入 v2
transform(v1.begin(), v1.end(), v2.begin(), [](int x) {
return x * 2;
});
// 输出 v2:2 4 6
for (int num : v2) {
cout << num << " ";
}
return 0;
}
10. STL 的六大组件
STL(标准模板库)通过六大组件的协同工作,实现了数据存储、操作和扩展的标准化,是 C++ 高效开发的核心工具。
1. 容器(Containers)
核心作用:管理数据集合,提供不同的数据结构(如数组、链表、树),满足不同的访问和修改需求。
常见容器及特点:
vector
(动态数组):- 内存连续,支持快速随机访问(
[]
操作符)。 - 尾部增删高效(
push_back
/pop_back
),中间增删低效(需移动元素)。 - 示例:
vector<int> v = {1, 2, 3};
- 内存连续,支持快速随机访问(
list
(双向链表):- 内存不连续,通过指针连接节点。
- 任意位置增删高效(
insert
/erase
),随机访问低效(需遍历)。 - 示例:
list<string> l = {"a", "b"};
map
(键值对映射):- 基于红黑树实现,按键自动排序(升序),键唯一。
- 支持快速查找(
O(log n)
),适合存储字典类数据。 - 示例:
map<int, string> m = {{1, "one"}, {2, "two"}};
2. 算法(Algorithms)
核心作用:提供通用操作接口(如排序、查找、遍历),通过迭代器与容器交互,实现 “算法与容器解耦”。
常见算法:
- 排序:
sort(v.begin(), v.end());
(默认升序)。 - 查找:
find(v.begin(), v.end(), 2);
(精确查找)。 - 遍历:
for_each(v.begin(), v.end(), [](int x) { ... });
。
3. 迭代器(Iterators)
核心作用:作为容器与算法的 “桥梁”,提供统一的访问接口(如 *
取值、++
移动),屏蔽不同容器的底层实现差异。
迭代器类型及特点:
- 随机访问迭代器(如
vector
):支持+n
、-n
操作,可快速跳转。 - 双向迭代器(如
list
、map
):仅支持++
、--
操作,双向遍历。 - 用法示例:
vector<int> v = {1, 2, 3}; // 遍历 vector for (vector<int>::iterator it = v.begin(); it != v.end(); ++it) { cout << *it << " "; }
4. 仿函数(Functors)
核心作用:重载 operator()
的类 / 结构体,行为类似函数,用于算法中的自定义逻辑(如排序规则、条件判断)。
特点:
- 可保存状态(成员变量),比普通函数更灵活。
- 与算法结合紧密(如
sort
的比较规则)。
示例:
// 自定义仿函数:降序排序
struct MyCompare {
bool operator()(int a, int b) const {
return a > b; // 大的在前
}
};
vector<int> v = {3, 1, 2};
// 用仿函数指定排序规则
sort(v.begin(), v.end(), MyCompare());
// 输出:3 2 1
5. 适配器(Adapters)
核心作用:对已有组件(容器、迭代器、仿函数)进行封装或转换,改变其接口或行为,满足特定需求。
常见适配器:
容器适配器:
stack
(栈):基于deque
实现,提供push
/pop
接口(后进先出)。queue
(队列):基于deque
实现,提供push
/front
接口(先进先出)。
函数适配器:
bind2nd(less<int>(), 5)
:将二元仿函数less<int>()
(比较a < b
)适配为一元函数(判断a < 5
)。
6. 空间配置器(Allocators)
核心作用:负责容器的内存管理(分配、释放、对象构造 / 析构),隐藏底层内存细节,提供高效的内存分配策略。
特点:
- 默认配置器
allocator<T>
:适用于大多数场景,无需手动指定。 - 自定义配置器:可实现内存池(减少内存碎片)、对齐分配等高级功能,需重载
allocate
(分配)和deallocate
(释放)方法。
示例(自定义配置器):
template <class T>
struct MyAllocator {
// 分配 n 个 T 类型的内存
T* allocate(size_t n) {
return (T*)malloc(n * sizeof(T));
}
// 释放内存
void deallocate(T* p, size_t n) {
free(p);
}
};
// 使用自定义配置器的 vector
vector<int, MyAllocator<int>> v;
11. 质变算法和非质变算法
根据算法是否修改容器元素内容,STL 算法可分为两类:
1. 质变算法(Mutating Algorithms)
核心特征:运行时会修改容器元素的内容或结构(如值替换、元素移动、删除元素)。
常见质变算法:
copy
:拷贝元素覆盖目标容器。replace
/replace_if
:替换元素值。sort
:排序元素(修改元素位置)。erase
:删除元素(修改容器大小)。
示例:
vector<int> v = {1, 2, 3};
// 替换元素(质变)
replace(v.begin(), v.end(), 2, 5); // v 变为 {1, 5, 3}
2. 非质变算法(Non-mutating Algorithms)
核心特征:运行时不修改元素本身,仅进行查询、统计、遍历等只读操作。
常见非质变算法:
find
/find_if
:查找元素(仅返回迭代器)。count
:统计元素出现次数。for_each
:遍历元素(若操作是只读)。max_element
:查找最大值(不修改元素)。
示例:
vector<int> v = {1, 2, 3};
// 查找元素(非质变)
auto it = find(v.begin(), v.end(), 2); // 仅返回迭代器,不修改 v