C++ 学习杂记00:标准模板库(STL)

概述

C++ 标准模板库(Standard Template Library,STL)是C++标准库的核心组成部分,提供了一组通用的模板类和函数,实现了常见的数据结构和算法。STL基于泛型编程思想,具有高度可复用性和效率。

核心组件

1. 容器(Containers)

容器用于存储和管理数据集合。

顺序容器
复制代码
复制代码
复制代码
#include <iostream>
#include <vector>
#include <deque>
#include <list>
#include <array>

// vector - 动态数组
void vector_example() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    // 添加元素
    vec.push_back(6);
    vec.insert(vec.begin(), 0);
    
    // 访问元素
    std::cout << "第一个元素: " << vec.front() << std::endl;
    std::cout << "最后一个元素: " << vec.back() << std::endl;
    std::cout << "第三个元素: " << vec[2] << std::endl;  // 不检查边界
    std::cout << "第三个元素: " << vec.at(2) << std::endl; // 检查边界
    
    // 遍历
    for (int num : vec) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    // 容量信息
    std::cout << "大小: " << vec.size() << std::endl;
    std::cout << "容量: " << vec.capacity() << std::endl;
}

// deque - 双端队列
void deque_example() {
    std::deque<int> dq = {2, 3, 4};
    
    // 两端操作
    dq.push_front(1);
    dq.push_back(5);
    
    for (int num : dq) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
}

// list - 双向链表
void list_example() {
    std::list<int> lst = {1, 2, 3, 4, 5};
    
    // 插入删除
    auto it = std::next(lst.begin(), 2); // 指向第三个元素
    lst.insert(it, 99);
    lst.erase(std::prev(lst.end())); // 删除最后一个元素
    
    // 链表特有操作
    lst.sort();
    lst.unique();
    
    for (int num : lst) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
}
关联容器
复制代码
复制代码
复制代码
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>

// set - 有序集合
void set_example() {
    std::set<int> s = {5, 2, 8, 1, 9};
    
    // 插入
    s.insert(3);
    s.insert(5); // 重复元素,不会插入
    
    // 查找
    auto it = s.find(3);
    if (it != s.end()) {
        std::cout << "找到元素: " << *it << std::endl;
    }
    
    // 遍历(有序)
    for (int num : s) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    // 多重集合允许重复
    std::multiset<int> ms = {1, 2, 2, 3, 3, 3};
    std::cout << "数字3的出现次数: " << ms.count(3) << std::endl;
}

// map - 有序映射
void map_example() {
    std::map<std::string, int> scores = {
        {"Alice", 90},
        {"Bob", 85},
        {"Charlie", 95}
    };
    
    // 插入
    scores["David"] = 88;
    scores.insert({"Eve", 92});
    
    // 访问
    std::cout << "Alice的分数: " << scores["Alice"] << std::endl;
    std::cout << "Bob的分数: " << scores.at("Bob") << std::endl;
    
    // 遍历
    for (const auto& pair : scores) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }
    
    // 查找
    auto it = scores.find("Charlie");
    if (it != scores.end()) {
        std::cout << "找到Charlie,分数: " << it->second << std::endl;
    }
}

// 无序容器(哈希表)
void unordered_example() {
    std::unordered_map<std::string, int> word_count;
    
    // 统计单词出现次数
    std::string text = "apple banana apple orange banana apple";
    std::string word;
    std::istringstream iss(text);
    
    while (iss >> word) {
        word_count[word]++;
    }
    
    for (const auto& pair : word_count) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }
}
容器适配器
复制代码
复制代码
复制代码
#include <stack>
#include <queue>

void adapter_example() {
    // stack - 栈
    std::stack<int> stk;
    stk.push(1);
    stk.push(2);
    stk.push(3);
    
    std::cout << "栈顶元素: " << stk.top() << std::endl;
    stk.pop();
    std::cout << "弹出后栈顶: " << stk.top() << std::endl;
    
    // queue - 队列
    std::queue<int> q;
    q.push(1);
    q.push(2);
    q.push(3);
    
    std::cout << "队首: " << q.front() << std::endl;
    std::cout << "队尾: " << q.back() << std::endl;
    q.pop();
    std::cout << "出队后队首: " << q.front() << std::endl;
    
    // priority_queue - 优先队列
    std::priority_queue<int> pq;
    pq.push(3);
    pq.push(1);
    pq.push(4);
    pq.push(2);
    
    std::cout << "优先队列(最大堆): ";
    while (!pq.empty()) {
        std::cout << pq.top() << " ";
        pq.pop();
    }
    std::cout << std::endl;
    
    // 最小堆
    std::priority_queue<int, std::vector<int>, std::greater<int>> min_pq;
    min_pq.push(3);
    min_pq.push(1);
    min_pq.push(4);
    
    std::cout << "最小堆: " << min_pq.top() << std::endl;
}

2. 迭代器(Iterators)

迭代器用于访问容器中的元素,提供统一的遍历接口。

复制代码
复制代码
复制代码
#include <iterator>
#include <algorithm>

void iterator_example() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    // 不同类型迭代器
    std::cout << "顺序遍历: ";
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    std::cout << "逆序遍历: ";
    for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    // 迭代器类别
    std::vector<int>::iterator random_it = vec.begin();  // 随机访问迭代器
    std::list<int>::iterator bidirectional_it;           // 双向迭代器
    
    // 迭代器操作
    auto it = vec.begin();
    std::advance(it, 2);  // 前进2个位置
    std::cout << "前进2位: " << *it << std::endl;
    
    // 计算距离
    auto dist = std::distance(vec.begin(), vec.end());
    std::cout << "容器大小: " << dist << std::endl;
}

3. 算法(Algorithms)

STL提供丰富的通用算法,大多数定义在 <algorithm>头文件中。

复制代码
复制代码
复制代码
#include <algorithm>
#include <numeric>

void algorithm_example() {
    std::vector<int> vec = {5, 2, 8, 1, 9, 3, 7, 4, 6};
    
    // 排序算法
    std::sort(vec.begin(), vec.end());
    std::cout << "排序后: ";
    for (int num : vec) std::cout << num << " ";
    std::cout << std::endl;
    
    // 查找算法
    auto it = std::find(vec.begin(), vec.end(), 7);
    if (it != vec.end()) {
        std::cout << "找到7,位置: " << std::distance(vec.begin(), it) << std::endl;
    }
    
    // 二分查找(需先排序)
    bool found = std::binary_search(vec.begin(), vec.end(), 5);
    std::cout << "二分查找5: " << (found ? "找到" : "未找到") << std::endl;
    
    // 计数
    int count_5 = std::count(vec.begin(), vec.end(), 5);
    std::cout << "数字5出现次数: " << count_5 << std::endl;
    
    // 累加
    int sum = std::accumulate(vec.begin(), vec.end(), 0);
    std::cout << "总和: " << sum << std::endl;
    
    // 变换
    std::vector<int> squared;
    std::transform(vec.begin(), vec.end(), std::back_inserter(squared),
                   [](int x) { return x * x; });
    
    std::cout << "平方: ";
    for (int num : squared) std::cout << num << " ";
    std::cout << std::endl;
    
    // 复制
    std::vector<int> copy_vec(vec.size());
    std::copy(vec.begin(), vec.end(), copy_vec.begin());
    
    // 删除和擦除模式
    vec.erase(std::remove_if(vec.begin(), vec.end(), 
                             [](int x) { return x % 2 == 0; }), // 删除偶数
              vec.end());
    
    std::cout << "删除偶数后: ";
    for (int num : vec) std::cout << num << " ";
    std::cout << std::endl;
    
    // 最大值最小值
    auto max_it = std::max_element(copy_vec.begin(), copy_vec.end());
    auto min_it = std::min_element(copy_vec.begin(), copy_vec.end());
    std::cout << "最大值: " << *max_it << ", 最小值: " << *min_it << std::endl;
}

4. 函数对象(Functors)和Lambda表达式

复制代码
复制代码
复制代码
#include <functional>

void functor_example() {
    // 内置函数对象
    std::plus<int> add;
    std::cout << "3 + 5 = " << add(3, 5) << std::endl;
    
    std::greater<int> greater_than;
    std::cout << "5 > 3? " << greater_than(5, 3) << std::endl;
    
    // 自定义函数对象
    struct Square {
        int operator()(int x) const {
            return x * x;
        }
    };
    
    Square square;
    std::cout << "4的平方: " << square(4) << std::endl;
    
    // Lambda表达式
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    // 使用Lambda过滤偶数
    std::cout << "偶数: ";
    std::for_each(vec.begin(), vec.end(), [](int x) {
        if (x % 2 == 0) {
            std::cout << x << " ";
        }
    });
    std::cout << std::endl;
    
    // 带捕获的Lambda
    int threshold = 3;
    std::cout << "大于" << threshold << "的数: ";
    std::for_each(vec.begin(), vec.end(), [threshold](int x) {
        if (x > threshold) {
            std::cout << x << " ";
        }
    });
    std::cout << std::endl;
    
    // Lambda排序
    std::vector<std::pair<int, std::string>> pairs = {
        {3, "three"}, {1, "one"}, {2, "two"}
    };
    
    std::sort(pairs.begin(), pairs.end(), 
              [](const auto& a, const auto& b) {
                  return a.first < b.first;
              });
    
    std::cout << "排序后: ";
    for (const auto& p : pairs) {
        std::cout << p.second << " ";
    }
    std::cout << std::endl;
}

5. 实用工具

复制代码
复制代码
复制代码
#include <utility>
#include <tuple>
#include <memory>

void utility_example() {
    // pair
    std::pair<int, std::string> p1 = {1, "Alice"};
    auto p2 = std::make_pair(2, "Bob");
    
    std::cout << "p1: " << p1.first << ", " << p1.second << std::endl;
    std::cout << "p2: " << p2.first << ", " << p2.second << std::endl;
    
    // tuple
    std::tuple<int, std::string, double> t1 = {1, "Apple", 2.5};
    auto t2 = std::make_tuple(2, "Banana", 1.8);
    
    std::cout << "t1: " << std::get<0>(t1) << ", " 
              << std::get<1>(t1) << ", " 
              << std::get<2>(t1) << std::endl;
    
    // 智能指针
    std::unique_ptr<int> ptr1 = std::make_unique<int>(42);
    std::cout << "unique_ptr: " << *ptr1 << std::endl;
    
    std::shared_ptr<int> ptr2 = std::make_shared<int>(100);
    std::shared_ptr<int> ptr3 = ptr2; // 共享所有权
    std::cout << "shared_ptr use_count: " << ptr2.use_count() << std::endl;
    
    // 移动语义
    std::vector<int> source = {1, 2, 3, 4, 5};
    std::vector<int> dest = std::move(source);
    
    std::cout << "移动后source大小: " << source.size() << std::endl;
    std::cout << "移动后dest大小: " << dest.size() << std::endl;
}

综合示例

复制代码
复制代码
复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <map>

// 学生信息结构
struct Student {
    int id;
    std::string name;
    double score;
    
    // 用于排序的比较函数
    bool operator<(const Student& other) const {
        return score > other.score; // 按分数降序
    }
};

void student_management_system() {
    std::vector<Student> students = {
        {101, "Alice", 85.5},
        {102, "Bob", 92.0},
        {103, "Charlie", 78.5},
        {104, "David", 88.0},
        {105, "Eve", 95.5}
    };
    
    // 1. 按分数排序
    std::sort(students.begin(), students.end());
    
    std::cout << "学生成绩排名:\n";
    for (const auto& student : students) {
        std::cout << "ID: " << student.id 
                  << ", 姓名: " << student.name 
                  << ", 分数: " << student.score << std::endl;
    }
    
    // 2. 查找特定学生
    auto it = std::find_if(students.begin(), students.end(),
                          [](const Student& s) { return s.name == "Charlie"; });
    if (it != students.end()) {
        std::cout << "\n找到Charlie,分数: " << it->score << std::endl;
    }
    
    // 3. 统计分数段
    int high_score = std::count_if(students.begin(), students.end(),
                                  [](const Student& s) { return s.score >= 90.0; });
    std::cout << "\n90分以上人数: " << high_score << std::endl;
    
    // 4. 计算平均分
    double total = std::accumulate(students.begin(), students.end(), 0.0,
                                  [](double sum, const Student& s) { 
                                      return sum + s.score; 
                                  });
    double average = total / students.size();
    std::cout << "平均分: " << average << std::endl;
    
    // 5. 使用map按ID快速查找
    std::map<int, Student> student_map;
    for (const auto& student : students) {
        student_map[student.id] = student;
    }
    
    int search_id = 102;
    auto map_it = student_map.find(search_id);
    if (map_it != student_map.end()) {
        std::cout << "\n通过ID查找: " << map_it->second.name 
                  << ", 分数: " << map_it->second.score << std::endl;
    }
}

int main() {
    std::cout << "===== STL综合示例 =====\n" << std::endl;
    
    // 运行各个示例
    std::cout << "1. 容器示例:" << std::endl;
    vector_example();
    std::cout << std::endl;
    
    std::cout << "2. 算法示例:" << std::endl;
    algorithm_example();
    std::cout << std::endl;
    
    std::cout << "3. 函数对象和Lambda示例:" << std::endl;
    functor_example();
    std::cout << std::endl;
    
    std::cout << "4. 学生管理系统示例:" << std::endl;
    student_management_system();
    
    return 0;
}

最佳实践

  1. 选择正确的容器

    • 需要随机访问:vectorarray

    • 频繁插入删除:listdeque

    • 需要自动排序:setmap

    • 需要快速查找:unordered_setunordered_map

  2. 使用算法代替手写循环

    复制代码
    复制代码
    复制代码
    // 不好
    for (int i = 0; i < vec.size(); ++i) {
        if (vec[i] > threshold) {
            // 处理
        }
    }
    
    // 更好
    std::for_each(vec.begin(), vec.end(), [threshold](int x) {
        if (x > threshold) {
            // 处理
        }
    });
  3. 使用智能指针管理资源

    复制代码
    复制代码
    复制代码
    // 避免
    MyClass* obj = new MyClass();
    // ... 使用obj
    delete obj;
    
    // 推荐
    auto obj = std::make_unique<MyClass>();
    // 自动释放内存
  4. 优先使用迭代器而不是索引

    复制代码
    复制代码
    复制代码
    // 可移植性更好
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        // 使用*it
    }

总结

STL是C++的核心特性之一,提供了强大而高效的工具集。掌握STL可以显著提高开发效率,写出更简洁、更安全、更高效的代码。建议通过实际项目不断练习,深入理解各个组件的特性和适用场景。

相关推荐
blog_wanghao2 小时前
条款03:尽可能使用const
c++
Rabitebla2 小时前
【C++】手撕日期类——运算符重载完全指南(含易错点+底层逻辑分析)
java·c语言·开发语言·数据结构·c++·算法·链表
艾莉丝努力练剑2 小时前
【Linux线程】Linux系统多线程(九):线程池实现(附代码示例)
linux·运维·服务器·c++·学习·架构
tankeven2 小时前
C++ 学习杂记01:C++ vector 容器详细
c++
艾莉丝努力练剑2 小时前
【Linux线程】Linux系统多线程(八):<策略模式>日志系统的封装实现
linux·运维·服务器·c++·学习·策略模式
盐焗鹌鹑蛋2 小时前
【C++】string模拟实现
c++
特种加菲猫2 小时前
C++进阶:模板深度解析与继承机制初探
开发语言·c++
旖-旎2 小时前
递归(快速幂)(5)
c++·算法·力扣·递归
大江东去浪淘尽千古风流人物5 小时前
【cuVSLAM】GPU 加速、多相机、实时视觉/视觉惯性 SLAM设计优势
c++·人工智能·数码相机·ubuntu·计算机视觉·augmented reality