C++、STL面试题总结(三)

发布于:2025-08-08 ⋅ 阅读:(17) ⋅ 点赞:(0)

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_castdynamic_castconst_castreinterpret_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_intersectionset_union 等)

STL 提供一系列针对有序集合(如 setvector 排序后)的算法,用于计算交集、并集、差集,简化集合操作。

示例:集合算法的使用
#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_intersectionset_unionset_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 容器(vectorlistmap 等)。

示例

#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 → intstring → 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 操作,可快速跳转。
  • 双向迭代器(如 listmap):仅支持 ++-- 操作,双向遍历。
  • 用法示例:
    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

https://github.com/0voice


网站公告

今日签到

点亮在社区的每一天
去签到