首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >《C++ 程序设计》第 10 章 - 泛型程序设计与 C++ 标准模板库

《C++ 程序设计》第 10 章 - 泛型程序设计与 C++ 标准模板库

作者头像
啊阿狸不会拉杆
发布2026-01-21 12:40:05
发布2026-01-21 12:40:05
800
举报

前言

        大家好!今天我们来深入学习《C++ 程序设计》第 10 章的内容 ——泛型程序设计与 C++ 标准模板库 (STL)。STL 是 C++ 中最强大的特性之一,它提供了一套丰富的模板类和函数,帮助我们高效地进行程序设计。本章将从泛型编程的基本概念出发,逐步讲解 STL 的核心组件:迭代器、容器、函数对象和算法,最后通过综合实例展示 STL 的实际应用。

10.1 泛型程序设计及 STL 的结构

10.1.1 泛型程序设计的基本概念

        泛型程序设计 (Generic Programming) 是一种独立于特定数据类型的编程范式,它专注于算法和数据结构的逻辑结构,而将具体的数据类型作为参数传入。这种思想的核心是代码复用算法通用化

泛型编程的优势

  • 代码复用:一份算法可以用于多种数据类型
  • 类型安全:在编译期进行类型检查
  • 效率高:避免了虚函数调用的开销,性能接近手写的专用代码
  • 灵活性:可以根据需求定制具体实现
10.1.2 STL 简介

C++ 标准模板库 (STL) 是泛型编程思想的典型实现,它主要包含以下四大核心组件

        STL 组件关系:算法通过迭代器操作容器中的数据,函数对象可以作为算法的参数定制算法行为,形成了 "容器存储数据,迭代器连接容器与算法,算法实现操作逻辑,函数对象扩展算法功能" 的完整体系。

10.2 迭代器

        迭代器 (Iterator) 是连接容器和算法的桥梁,它提供了一种统一的方式来访问容器中的元素,而无需暴露容器的内部实现细节。

10.2.1 输入流迭代器和输出流迭代器

        输入流迭代器 (istream_iterator) 和输出流迭代器 (ostream_iterator) 是特殊的迭代器,用于将流作为容器处理。

代码语言:javascript
复制
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>

int main() {
    // 输入流迭代器:从标准输入读取整数
    std::cout << "请输入一些整数(按Ctrl+Z结束):" << std::endl;
    std::istream_iterator<int> in_iter(std::cin);
    std::istream_iterator<int> end_of_stream;  // 流结束迭代器
    
    // 输出流迭代器:将数据输出到标准输出,每个元素后加空格
    std::ostream_iterator<int> out_iter(std::cout, " ");
    
    // 使用算法将输入流数据复制到输出流
    std::copy(in_iter, end_of_stream, out_iter);
    std::cout << std::endl;
    
    return 0;
}
10.2.2 迭代器的分类

STL 将迭代器分为 5 类,每类支持不同的操作集,形成了一个继承关系:

  • 输入迭代器:只读,支持 ++、* 操作,如istream_iterator
  • 输出迭代器:只写,支持 ++、* 操作,如ostream_iterator
  • 前向迭代器:可读写,支持 ++、*、==、!=,如forward_list的迭代器
  • 双向迭代器:在前向迭代器基础上增加 -- 操作,如listsetmap的迭代器
  • 随机访问迭代器:支持所有迭代器操作,还支持 +=、-=、[] 等随机访问操作,如vectordeque的迭代器
10.2.3 迭代器的区间

        STL 中算法通常使用半开区间[first, last)来表示元素范围,即包含first指向的元素,但不包含last指向的元素。这种表示方式有以下优势:

  • 空区间表示简单:first == last
  • 便于表示子区间:[first, mid)[mid, last)无重叠且覆盖原区间
代码语言:javascript
复制
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    // 区间[begin, end)包含所有元素
    std::cout << "所有元素: ";
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    // 区间[begin, begin+3)包含前3个元素
    std::cout << "前3个元素: ";
    for (auto it = vec.begin(); it != vec.begin() + 3; ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    return 0;
}
10.2.4 迭代器的辅助函数

STL 提供了三个常用的迭代器辅助函数:

  • advance(it, n):将迭代器 it 前进 n 个位置
  • distance(first, last):计算两个迭代器之间的距离
  • iter_swap(it1, it2):交换两个迭代器指向的元素
代码语言:javascript
复制
#include <vector>
#include <iterator>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> vec = {10, 20, 30, 40, 50};
    auto it = vec.begin();
    
    // 前进2个位置
    std::advance(it, 2);
    std::cout << "前进2个位置后的值: " << *it << std::endl;  // 输出30
    
    // 计算距离
    int dist = std::distance(vec.begin(), vec.end());
    std::cout << "容器长度: " << dist << std::endl;  // 输出5
    
    // 交换元素
    std::iter_swap(vec.begin(), vec.end() - 1);
    std::cout << "交换首尾元素后: ";
    for (auto num : vec) {
        std::cout << num << " ";  // 输出50 20 30 40 10
    }
    std::cout << std::endl;
    
    return 0;
}

10.3 容器的基本功能与分类

        容器 (Container) 是 STL 中用于存储数据的类模板,提供了管理对象集合的标准化接口。

容器的基本功能

  • 元素的插入、删除和访问
  • 容器大小的管理
  • 迭代器的获取(用于遍历元素)
  • 交换两个容器的内容

容器的分类

  • 顺序容器:元素按插入顺序排列,如vectorlist
  • 关联容器:元素按关键字排序,如setmap
  • 容器适配器:对现有容器进行包装,提供特定接口,如stackqueue

10.4 顺序容器

10.4.1 顺序容器的基本功能

所有顺序容器都提供统一的接口,包括:

  • begin()/end():获取迭代器
  • push_back():在尾部插入元素
  • size():获取元素个数
  • empty():判断是否为空
  • clear():清空容器
  • insert():插入元素
  • erase():删除元素
10.4.2 5 种顺序容器的特性

STL 提供 5 种顺序容器,各有不同的特性和适用场景:

容器类型

内部实现

随机访问

尾部插入 / 删除

中间插入 / 删除

内存连续性

vector

动态数组

支持 (O (1))

快 (amortized O (1) )

慢 (O (n))

连续

deque

分段数组

支持 (O (1))

快 (O (1))

慢 (O (n))

不连续

list

双向链表

不支持

快 (O (1))

快 (O (1))

不连续

forward_list

单向链表

不支持

不支持

快 (O (1))

不连续

array

静态数组

支持 (O (1))

不支持

不支持

连续

代码语言:javascript
复制
#include <vector>
#include <deque>
#include <list>
#include <array>
#include <forward_list>
#include <iostream>

int main() {
    // vector示例
    std::vector<int> vec = {1, 2, 3};
    vec.push_back(4);  // 尾部插入
    std::cout << "vector: ";
    for (int v : vec) std::cout << v << " ";  // 1 2 3 4
    std::cout << std::endl;
    
    // deque示例
    std::deque<int> dq = {10, 20};
    dq.push_front(5);  // 头部插入
    dq.push_back(30);  // 尾部插入
    std::cout << "deque: ";
    for (int d : dq) std::cout << d << " ";  // 5 10 20 30
    std::cout << std::endl;
    
    // list示例
    std::list<int> lst = {100, 200};
    lst.insert(lst.begin(), 50);  // 头部插入
    lst.insert(++lst.begin(), 75);  // 中间插入
    std::cout << "list: ";
    for (int l : lst) std::cout << l << " ";  // 50 75 100 200
    std::cout << std::endl;
    
    // array示例(固定大小)
    std::array<int, 3> arr = {1, 2, 3};
    arr[1] = 5;  // 修改元素
    std::cout << "array: ";
    for (int a : arr) std::cout << a << " ";  // 1 5 3
    std::cout << std::endl;
    
    return 0;
}
10.4.3 顺序容器的插入迭代器

        插入迭代器是一种特殊的迭代器,用于将元素插入容器的特定位置,包括:

  • back_inserter:在容器尾部插入
  • front_inserter:在容器头部插入(仅支持dequelist
  • inserter:在指定位置插入
代码语言:javascript
复制
#include <vector>
#include <list>
#include <iterator>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3};
    std::list<int> lst;
    
    // 使用back_inserter在尾部插入
    std::copy(vec.begin(), vec.end(), std::back_inserter(lst));
    std::cout << "list after back_inserter: ";
    for (int l : lst) std::cout << l << " ";  // 1 2 3
    std::cout << std::endl;
    
    // 使用front_inserter在头部插入
    std::list<int> lst2;
    std::copy(vec.begin(), vec.end(), std::front_inserter(lst2));
    std::cout << "list after front_inserter: ";
    for (int l : lst2) std::cout << l << " ";  // 3 2 1
    std::cout << std::endl;
    
    // 使用inserter在指定位置插入
    std::vector<int> vec2 = {10, 20, 30};
    std::copy(vec.begin(), vec.end(), std::inserter(vec2, vec2.begin() + 1));
    std::cout << "vector after inserter: ";
    for (int v : vec2) std::cout << v << " ";  // 10 1 2 3 20 30
    std::cout << std::endl;
    
    return 0;
}
10.4.4 顺序容器的适配器

        容器适配器对现有容器进行包装,提供特定的数据结构接口:

  • stack:栈,后进先出 (LIFO),默认基于deque实现
  • queue:队列,先进先出 (FIFO),默认基于deque实现
  • priority_queue:优先队列,默认基于vector实现,每次出队最大元素
代码语言:javascript
复制
#include <stack>
#include <queue>
#include <iostream>

int main() {
    // 栈示例
    std::stack<int> st;
    st.push(10);
    st.push(20);
    st.push(30);
    std::cout << "stack elements: ";
    while (!st.empty()) {
        std::cout << st.top() << " ";  // 30 20 10
        st.pop();
    }
    std::cout << std::endl;
    
    // 队列示例
    std::queue<int> q;
    q.push(10);
    q.push(20);
    q.push(30);
    std::cout << "queue elements: ";
    while (!q.empty()) {
        std::cout << q.front() << " ";  // 10 20 30
        q.pop();
    }
    std::cout << std::endl;
    
    // 优先队列示例
    std::priority_queue<int> pq;
    pq.push(30);
    pq.push(10);
    pq.push(20);
    std::cout << "priority_queue elements: ";
    while (!pq.empty()) {
        std::cout << pq.top() << " ";  // 30 20 10
        pq.pop();
    }
    std::cout << std::endl;
    
    return 0;
}

10.5 关联容器

10.5.1 关联容器的分类及基本功能

        关联容器存储的是键值对(key-value pair),元素按关键字自动排序,支持快速查找 (O (log n))。关联容器的基本功能包括:

  • insert():插入键值对
  • erase():删除键值对
  • find():按关键字查找
  • count():统计关键字出现次数
  • size()/empty():容器大小相关操作
10.5.2 集合

   set是存储唯一关键字的集合,元素既是关键字也是值,默认按升序排列:

代码语言:javascript
复制
#include <set>
#include <iostream>

int main() {
    // 创建set并插入元素
    std::set<int> s;
    s.insert(30);
    s.insert(10);
    s.insert(20);
    s.insert(10);  // 重复元素,不会被插入
    
    // 遍历set(自动排序)
    std::cout << "set elements: ";
    for (int num : s) {
        std::cout << num << " ";  // 10 20 30
    }
    std::cout << std::endl;
    
    // 查找元素
    auto it = s.find(20);
    if (it != s.end()) {
        std::cout << "找到元素: " << *it << std::endl;  // 找到元素: 20
    }
    
    // 删除元素
    s.erase(20);
    std::cout << "删除20后: ";
    for (int num : s) {
        std::cout << num << " ";  // 10 30
    }
    std::cout << std::endl;
    
    return 0;
}
10.5.3 映射

   map存储键值对,每个关键字唯一,通过[]at()可以快速访问值:

代码语言:javascript
复制
#include <map>
#include <string>
#include <iostream>

int main() {
    // 创建map存储姓名和年龄
    std::map<std::string, int> person_ages;
    
    // 插入元素
    person_ages["Alice"] = 25;
    person_ages["Bob"] = 30;
    person_ages.insert({"Charlie", 35});
    
    // 遍历map
    std::cout << "map elements: " << std::endl;
    for (const auto& pair : person_ages) {
        std::cout << pair.first << ": " << pair.second << "岁" << std::endl;
    }
    // 输出:
    // Alice: 25岁
    // Bob: 30岁
    // Charlie: 35岁
    
    // 访问元素
    std::cout << "Alice的年龄: " << person_ages["Alice"] << std::endl;  // 25
    
    // 查找元素
    auto it = person_ages.find("Bob");
    if (it != person_ages.end()) {
        std::cout << "找到Bob: " << it->second << "岁" << std::endl;  // 30
    }
    
    return 0;
}
10.5.4 多重集合与多重映射
  • multiset:允许存储重复关键字的集合
  • multimap:允许存储重复关键字的映射
代码语言:javascript
复制
#include <iostream>
#include <set>       // 包含multiset所需的头文件
#include <map>       // 包含multimap所需的头文件
#include <string>

int main() {
    // multiset示例:允许存储重复元素的集合
    std::multiset<int> ms;
    ms.insert(20);
    ms.insert(10);
    ms.insert(20);  // 允许插入重复元素
    ms.insert(30);
    
    std::cout << "multiset elements: ";
    // 元素会自动排序
    for (int num : ms) {
        std::cout << num << " ";  // 输出:10 20 20 30
    }
    std::cout << std::endl;
    
    // 统计元素出现的次数
    std::cout << "数字20出现的次数: " << ms.count(20) << std::endl;  // 输出:2
    
    // multimap示例:允许键重复的映射
    std::multimap<std::string, std::string> courses;
    courses.insert({"Math", "Alice"});
    courses.insert({"Math", "Bob"});      // 允许相同的键
    courses.insert({"Physics", "Charlie"});
    courses.insert({"Math", "David"});    // 允许相同的键
    
    std::cout << "Math课程的学生: ";
    // 查找所有键为"Math"的元素
    auto range = courses.equal_range("Math");
    for (auto it = range.first; it != range.second; ++it) {
        std::cout << it->second << " ";  // 输出:Alice Bob David
    }
    std::cout << std::endl;
    
    return 0;
}
10.5.5 无序容器

        C++11 引入了无序容器,基于哈希表实现,包括unordered_setunordered_mapunordered_multisetunordered_multimap。特点是:

  • 插入、查找、删除操作平均时间复杂度为 O (1)
  • 元素无序排列
  • 需要关键字支持哈希函数
代码语言:javascript
复制
#include <unordered_set>
#include <unordered_map>
#include <iostream>

int main() {
    // unordered_set示例
    std::unordered_set<std::string> us;
    us.insert("apple");
    us.insert("banana");
    us.insert("cherry");
    
    std::cout << "unordered_set elements: ";
    for (const auto& fruit : us) {
        std::cout << fruit << " ";  // 顺序不确定
    }
    std::cout << std::endl;
    
    // unordered_map示例
    std::unordered_map<int, std::string> um;
    um[1] = "one";
    um[2] = "two";
    um[3] = "three";
    
    std::cout << "unordered_map elements: ";
    for (const auto& pair : um) {
        std::cout << pair.first << ":" << pair.second << " ";  // 顺序不确定
    }
    std::cout << std::endl;
    
    return 0;
}

10.6 函数对象

10.6.1 函数对象的概念

        函数对象 (Function Object) 是重载了函数调用运算符() 的类对象,它可以像函数一样被调用。相比普通函数,函数对象可以携带状态。

STL 提供了一些预定义函数对象,如:

  • std::plus:加法
  • std::minus:减法
  • std::greater:大于比较
代码语言:javascript
复制
#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>

// 自定义函数对象:计算累加和
class Adder {
private:
    int sum;
public:
    Adder() : sum(0) {}
    
    // 重载函数调用运算符
    void operator()(int num) {
        sum += num;
    }
    
    int get_sum() const {
        return sum;
    }
};

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    // 使用自定义函数对象
    Adder adder;
    adder = std::for_each(vec.begin(), vec.end(), adder);
    std::cout << "总和: " << adder.get_sum() << std::endl;  // 15
    
    // 使用STL预定义函数对象
    std::vector<int> vec2 = {3, 1, 4, 1, 5};
    // 使用greater排序,实现降序
    std::sort(vec2.begin(), vec2.end(), std::greater<int>());
    
    std::cout << "降序排列: ";
    for (int num : vec2) {
        std::cout << num << " ";  // 5 4 3 1 1
    }
    std::cout << std::endl;
    
    return 0;
}
10.6.2 lambda 表达式

C++11 引入了 lambda 表达式,允许在代码中定义匿名函数对象,语法为:

代码语言:javascript
复制
[capture](parameters) -> return_type { body }
代码语言:javascript
复制
#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // 使用lambda表达式过滤偶数
    std::cout << "偶数: ";
    std::for_each(vec.begin(), vec.end(), [](int num) {
        if (num % 2 == 0) {
            std::cout << num << " ";  // 2 4 6 8 10
        }
    });
    std::cout << std::endl;
    
    // 使用lambda表达式捕获外部变量
    int threshold = 5;
    std::cout << "大于" << threshold << "的数: ";
    std::for_each(vec.begin(), vec.end(), [threshold](int num) {
        if (num > threshold) {
            std::cout << num << " ";  // 6 7 8 9 10
        }
    });
    std::cout << std::endl;
    
    // 使用lambda表达式修改外部变量(需要使用引用捕获)
    int sum = 0;
    std::for_each(vec.begin(), vec.end(), [&sum](int num) {
        sum += num;
    });
    std::cout << "总和: " << sum << std::endl;  // 55
    
    return 0;
}
10.6.3 函数对象参数绑定

std::bind可以将函数或函数对象与部分参数绑定,生成新的函数对象:

代码语言:javascript
复制
#include <functional>
#include <iostream>
#include <vector>  // 新增:包含vector头文件

// 普通函数
int add(int a, int b, int c) {
    return a + b + c;
}

int main() {
    // 绑定add函数的前两个参数为10和20
    auto add10_20 = std::bind(add, 10, 20, std::placeholders::_1);
    std::cout << "10 + 20 + 30 = " << add10_20(30) << std::endl;  // 60
    
    // 绑定add函数的第一个和第三个参数
    auto add_x_5 = std::bind(add, std::placeholders::_1, 5, std::placeholders::_2);
    std::cout << "20 + 5 + 35 = " << add_x_5(20, 35) << std::endl;  // 60
    
    // 绑定成员函数
    std::vector<int> vec = {1, 2, 3};
    // 绑定vector的size()成员函数
    auto print_size = std::bind(&std::vector<int>::size, std::placeholders::_1);
    std::cout << "vector大小: " << print_size(vec) << std::endl;  // 3
    
    return 0;
}

10.7 算法

10.7.1 STL 算法基础

STL 算法是一系列模板函数,用于操作容器中的元素,主要分为:

  • 不可变序列算法:不修改元素内容
  • 可变序列算法:修改元素内容
  • 排序和搜索算法
  • 数值算法

算法通常通过迭代器操作容器,不直接依赖容器类型,具有高度通用性。

10.7.2 不可变序列算法

不可变序列算法用于查询或分析元素,不修改容器内容:

  • find():查找元素
  • count():统计元素出现次数
  • for_each():遍历元素
  • equal():比较两个区间是否相等
  • max_element()/min_element():查找最大 / 小元素
代码语言:javascript
复制
#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6};
    
    // 查找元素5
    auto it = std::find(vec.begin(), vec.end(), 5);
    if (it != vec.end()) {
        std::cout << "找到元素5,位置: " << it - vec.begin() << std::endl;  // 4
    }
    
    // 统计元素1出现的次数
    int count = std::count(vec.begin(), vec.end(), 1);
    std::cout << "元素1出现的次数: " << count << std::endl;  // 2
    
    // 查找最大值和最小值
    auto max_it = std::max_element(vec.begin(), vec.end());
    auto min_it = std::min_element(vec.begin(), vec.end());
    std::cout << "最大值: " << *max_it << ", 最小值: " << *min_it << std::endl;  // 9, 1
    
    return 0;
}
10.7.3 可变序列算法

可变序列算法会修改容器中的元素:

  • copy():复制元素
  • transform():转换元素
  • replace():替换元素
  • fill():填充元素
  • remove():删除元素
代码语言:javascript
复制
#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    std::vector<int> vec2(5);
    
    // 复制元素
    std::copy(vec.begin(), vec.end(), vec2.begin());
    std::cout << "复制后的vec2: ";
    for (int num : vec2) std::cout << num << " ";  // 1 2 3 4 5
    std::cout << std::endl;
    
    // 转换元素(每个元素乘以2)
    std::transform(vec.begin(), vec.end(), vec.begin(), [](int x) { return x * 2; });
    std::cout << "转换后的vec: ";
    for (int num : vec) std::cout << num << " ";  // 2 4 6 8 10
    std::cout << std::endl;
    
    // 替换元素(将4替换为0)
    std::replace(vec.begin(), vec.end(), 4, 0);
    std::cout << "替换后的vec: ";
    for (int num : vec) std::cout << num << " ";  // 2 0 6 8 10
    std::cout << std::endl;
    
    return 0;
}
10.7.4 排序和搜索算法
  • sort():排序元素
  • stable_sort():稳定排序
  • binary_search():二分查找
  • lower_bound()/upper_bound():查找下界 / 上界
  • merge():合并两个有序区间
代码语言:javascript
复制
#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6};
    
    // 排序
    std::sort(vec.begin(), vec.end());
    std::cout << "排序后: ";
    for (int num : vec) std::cout << num << " ";  // 1 1 2 3 4 5 6 9
    std::cout << std::endl;
    
    // 二分查找
    bool found = std::binary_search(vec.begin(), vec.end(), 5);
    std::cout << "是否找到5: " << (found ? "是" : "否") << std::endl;  // 是
    
    // 查找下界和上界
    auto lower = std::lower_bound(vec.begin(), vec.end(), 1);
    auto upper = std::upper_bound(vec.begin(), vec.end(), 1);
    std::cout << "元素1的范围: [" << lower - vec.begin() << ", " 
              << upper - vec.begin() << ")" << std::endl;  // [0, 2)
    
    // 合并两个有序向量
    std::vector<int> vec1 = {1, 3, 5};
    std::vector<int> vec2 = {2, 4, 6};
    std::vector<int> merged(6);
    std::merge(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), merged.begin());
    std::cout << "合并后: ";
    for (int num : merged) std::cout << num << " ";  // 1 2 3 4 5 6
    std::cout << std::endl;
    
    return 0;
}
10.7.5 数值算法

数值算法用于数值计算,定义在<numeric>头文件中:

  • accumulate():累加元素
  • inner_product():计算内积
  • partial_sum():计算前缀和
  • iota():生成连续值
代码语言:javascript
复制
#include <vector>
#include <numeric>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    // 累加元素
    int sum = std::accumulate(vec.begin(), vec.end(), 0);
    std::cout << "总和: " << sum << std::endl;  // 15
    
    // 累加元素(乘以2)
    int sum2 = std::accumulate(vec.begin(), vec.end(), 0, 
                              [](int total, int num) { return total + num * 2; });
    std::cout << "每个元素乘2后总和: " << sum2 << std::endl;  // 30
    
    // 计算内积
    std::vector<int> vec2 = {10, 20, 30, 40, 50};
    int dot_product = std::inner_product(vec.begin(), vec.end(), vec2.begin(), 0);
    std::cout << "内积: " << dot_product << std::endl;  // 1*10 + 2*20 + ... +5*50 = 550
    
    // 计算前缀和
    std::vector<int> prefix_sums(vec.size());
    std::partial_sum(vec.begin(), vec.end(), prefix_sums.begin());
    std::cout << "前缀和: ";
    for (int num : prefix_sums) std::cout << num << " ";  // 1 3 6 10 15
    std::cout << std::endl;
    
    return 0;
}

10.8 综合实例 —— 对个人银行账户管理程序的改进

下面我们使用 STL 改进一个个人银行账户管理程序,使用map存储账户信息,使用算法进行账户查询、排序等操作:

代码语言:javascript
复制
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <numeric>
#include <iomanip>
#include <iterator>  // 新增:包含back_inserter所需的头文件

// 账户类
class Account {
private:
    std::string account_id;  // 账户ID
    std::string name;        // 账户名
    double balance;          // 余额

public:
    // 构造函数
    Account(std::string id, std::string n, double b)
        : account_id(std::move(id)), name(std::move(n)), balance(b) {}

    // 获取账户ID
    const std::string& get_id() const { return account_id; }
    
    // 获取账户名
    const std::string& get_name() const { return name; }
    
    // 获取余额
    double get_balance() const { return balance; }
    
    // 存款
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
    
    // 取款
    bool withdraw(double amount) {
        if (amount > 0 && balance >= amount) {
            balance -= amount;
            return true;
        }
        return false;
    }
    
    // 显示账户信息
    void display() const {
        std::cout << std::left << std::setw(10) << account_id
                  << std::setw(15) << name
                  << std::fixed << std::setprecision(2) << balance << std::endl;
    }
};

// 银行类,使用STL容器管理账户
class Bank {
private:
    // 使用map存储账户,key为账户ID,value为Account对象
    std::map<std::string, Account> accounts;

public:
    // 添加账户
    void add_account(const Account& acc) {
        accounts.insert({acc.get_id(), acc});
    }
    
    // 查找账户
    Account* find_account(const std::string& id) {
        auto it = accounts.find(id);
        if (it != accounts.end()) {
            return &(it->second);
        }
        return nullptr;
    }
    
    // 显示所有账户
    void display_all() const {
        std::cout << std::left << std::setw(10) << "账户ID"
                  << std::setw(15) << "账户名"
                  << "余额" << std::endl;
        std::cout << "-----------------------------------------" << std::endl;
        for (const auto& pair : accounts) {
            pair.second.display();
        }
    }
    
    // 获取所有账户余额总和
    double get_total_balance() const {
        // 使用accumulate算法计算总余额
        return std::accumulate(accounts.begin(), accounts.end(), 0.0,
            [](double total, const std::pair<std::string, Account>& pair) {
                return total + pair.second.get_balance();
            });
    }
    
    // 获取余额大于指定值的账户
    std::vector<Account> get_high_balance_accounts(double threshold) const {
        std::vector<Account> result;
        
        // 使用for_each替代copy_if,手动处理元素提取
        std::for_each(accounts.begin(), accounts.end(),
            [threshold, &result](const std::pair<std::string, Account>& pair) {
                if (pair.second.get_balance() > threshold) {
                    result.push_back(pair.second);
                }
            });
        
        return result;
    }
    
    // 按余额排序账户
    std::vector<Account> get_sorted_by_balance() const {
        std::vector<Account> result;
        
        // 复制所有账户到vector
        for (const auto& pair : accounts) {
            result.push_back(pair.second);
        }
        
        // 使用sort算法按余额排序
        std::sort(result.begin(), result.end(),
            [](const Account& a, const Account& b) {
                return a.get_balance() < b.get_balance();
            });
        
        return result;
    }
};

int main() {
    // 创建银行对象
    Bank bank;
    
    // 添加账户
    bank.add_account(Account("1001", "Alice", 1000.50));
    bank.add_account(Account("1002", "Bob", 2500.75));
    bank.add_account(Account("1003", "Charlie", 800.00));
    bank.add_account(Account("1004", "David", 5000.00));
    
    // 显示所有账户
    std::cout << "所有账户信息:" << std::endl;
    bank.display_all();
    std::cout << std::endl;
    
    // 显示总余额
    std::cout << "所有账户总余额: " << std::fixed << std::setprecision(2)
              << bank.get_total_balance() << std::endl << std::endl;
    
    // 查找账户并操作
    Account* alice = bank.find_account("1001");
    if (alice) {
        std::cout << "找到Alice的账户,存款前余额: " << alice->get_balance() << std::endl;
        alice->deposit(500);
        std::cout << "存款500后余额: " << alice->get_balance() << std::endl << std::endl;
    }
    
    // 查找高余额账户
    double threshold = 2000.0;
    std::vector<Account> high_balance = bank.get_high_balance_accounts(threshold);
    std::cout << "余额大于" << threshold << "的账户:" << std::endl;
    for (const auto& acc : high_balance) {
        acc.display();
    }
    std::cout << std::endl;
    
    // 按余额排序账户
    std::vector<Account> sorted = bank.get_sorted_by_balance();
    std::cout << "按余额排序的账户:" << std::endl;
    for (const auto& acc : sorted) {
        acc.display();
    }
    
    return 0;
}

10.9 深度探索

10.9.1 swap

swap函数用于交换两个对象的值,STL 为容器提供了高效的特化版本:

  • 容器的swap操作通常是常数时间复杂度
  • 交换后迭代器仍然有效(指向原来的容器)
代码语言:javascript
复制
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec1 = {1, 2, 3};
    std::vector<int> vec2 = {4, 5, 6, 7};
    
    std::cout << "交换前: " << std::endl;
    std::cout << "vec1: ";
    for (int num : vec1) std::cout << num << " ";  // 1 2 3
    std::cout << std::endl;
    std::cout << "vec2: ";
    for (int num : vec2) std::cout << num << " ";  // 4 5 6 7
    std::cout << std::endl;
    
    // 交换两个vector
    vec1.swap(vec2);
    
    std::cout << "交换后: " << std::endl;
    std::cout << "vec1: ";
    for (int num : vec1) std::cout << num << " ";  // 4 5 6 7
    std::cout << std::endl;
    std::cout << "vec2: ";
    for (int num : vec2) std::cout << num << " ";  // 1 2 3
    std::cout << std::endl;
    
    return 0;
}
10.9.2 STL 组件的类型特征与 STL 的扩展

C++11 引入了<type_traits>头文件,提供了编译期类型信息查询和转换的工具:

代码语言:javascript
复制
#include <type_traits>
#include <iostream>
#include <vector>
#include <list>

int main() {
    // 检查类型特性
    std::cout << "int是否为算术类型: " << std::is_arithmetic<int>::value << std::endl;  // 1
    std::cout << "int是否为浮点类型: " << std::is_floating_point<int>::value << std::endl;  // 0
    std::cout << "vector<int>是否为容器: " << std::is_class<std::vector<int>>::value << std::endl;  // 1
    
    // 类型转换
    using signed_type = std::make_signed<unsigned int>::type;
    std::cout << "unsigned int的有符号版本: " << typeid(signed_type).name() << std::endl;  // int
    
    return 0;
}
10.9.3 Boost 简介

        Boost 是一个开源 C++ 库集合,提供了许多 STL 的扩展功能,是 C++ 标准的重要试验场。许多 Boost 库已被纳入 C++ 标准(如智能指针、正则表达式)。

常用 Boost 库:

  • Boost.Asio:网络和异步 I/O
  • Boost.PointerContainer:智能指针容器
  • Boost.Graph:图算法
  • Boost.Python:C++ 与 Python 交互
  • Boost.Thread:多线程支持

Boost 的使用示例(假设已安装 Boost 库):

代码语言:javascript
复制
#include <boost/version.hpp>
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <string>
#ifdef _WIN32
#include <windows.h>
#endif

int main() {
    // 设置控制台编码为UTF-8
#ifdef _WIN32
    SetConsoleOutputCP(65001);
#endif
    
    // 原有代码
    std::cout << "Boost版本: " << BOOST_VERSION / 100000 << "." 
              << (BOOST_VERSION / 100) % 1000 << "." 
              << BOOST_VERSION % 100 << std::endl;
    
    std::string str = "Hello, Boost!";
    boost::algorithm::to_upper(str);
    std::cout << "转换为大写: " << str << std::endl;
    
    std::string str2 = "  trim this string  ";
    boost::algorithm::trim(str2);
    std::cout << "去除空格后: '" << str2 << "'" << std::endl;
    
    // 添加暂停代码:等待用户按任意键后再退出
    std::cout << "\n程序运行完毕,按任意键退出..." << std::endl;
    std::cin.get();  // 等待用户输入一个字符
    
    return 0;
}

10.10 小结

本章我们系统学习了 C++ 泛型程序设计和标准模板库 (STL) 的核心内容,主要包括:

  1. 泛型程序设计:独立于数据类型的编程范式,通过模板实现代码复用
  2. STL 的核心组件:容器、迭代器、函数对象和算法的协同工作
  3. 迭代器:连接容器和算法的桥梁,分为 5 类不同功能的迭代器
  4. 容器
    • 顺序容器:vectordequelist等,按插入顺序存储元素
    • 关联容器:setmap等,按关键字排序,支持快速查找
    • 容器适配器:stackqueue等,提供特定数据结构接口
  5. 函数对象:重载()的类对象,以及 C++11 的 lambda 表达式,增强算法灵活性
  6. 算法:STL 提供的通用算法,包括查找、排序、数值计算等
  7. 综合应用:使用 STL 改进实际程序,提高开发效率和代码质量

        STL 是 C++ 编程的重要工具,掌握 STL 可以大幅提高编程效率和代码质量。通过泛型编程思想,我们可以编写更通用、更灵活、更高效的代码。建议多实践、多使用 STL 组件,深入理解其设计思想和实现原理。

        希望本章内容对你有所帮助,欢迎在评论区交流学习心得!

        代码说明:本文所有代码均基于 C++11 标准编写,可使用 GCC 或 Clang 编译器编译运行(需添加-std=c++11或更高版本选项)。Boost 库示例需要先安装 Boost 库才能编译。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 10.1 泛型程序设计及 STL 的结构
    • 10.1.1 泛型程序设计的基本概念
    • 10.1.2 STL 简介
  • 10.2 迭代器
    • 10.2.1 输入流迭代器和输出流迭代器
    • 10.2.2 迭代器的分类
    • 10.2.3 迭代器的区间
    • 10.2.4 迭代器的辅助函数
  • 10.3 容器的基本功能与分类
  • 10.4 顺序容器
    • 10.4.1 顺序容器的基本功能
    • 10.4.2 5 种顺序容器的特性
    • 10.4.3 顺序容器的插入迭代器
    • 10.4.4 顺序容器的适配器
  • 10.5 关联容器
    • 10.5.1 关联容器的分类及基本功能
    • 10.5.2 集合
    • 10.5.3 映射
    • 10.5.4 多重集合与多重映射
    • 10.5.5 无序容器
  • 10.6 函数对象
    • 10.6.1 函数对象的概念
    • 10.6.2 lambda 表达式
    • 10.6.3 函数对象参数绑定
  • 10.7 算法
    • 10.7.1 STL 算法基础
    • 10.7.2 不可变序列算法
    • 10.7.3 可变序列算法
    • 10.7.4 排序和搜索算法
    • 10.7.5 数值算法
  • 10.8 综合实例 —— 对个人银行账户管理程序的改进
  • 10.9 深度探索
    • 10.9.1 swap
    • 10.9.2 STL 组件的类型特征与 STL 的扩展
    • 10.9.3 Boost 简介
  • 10.10 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档