C++学习:六个月从基础到就业——C++学习之旅:STL迭代器系统

C++学习:六个月从基础到就业------C++学习之旅:STL迭代器系统

本文是我C++学习之旅系列的第二十四篇技术文章,也是第二阶段"C++进阶特性"的第二篇,主要介绍C++ STL迭代器系统。查看完整系列目录了解更多内容。

引言

在上一篇文章中,我们详细探讨了STL容器,了解了各种容器的特性和用法。今天,我们将深入研究STL的另一个核心组件------迭代器系统。迭代器是连接容器和算法的桥梁,它使得算法可以以统一的方式访问不同类型的容器,是STL设计中最为精妙的部分之一。

什么是迭代器?

迭代器是一种行为类似于指针的对象,提供了一种访问容器中元素的方式,而不需要暴露容器的内部实现。通过迭代器,我们可以遍历集合、访问集合中的元素,并且根据需要修改这些元素。

迭代器抽象了底层容器的实现细节,提供了一套统一的接口,使得算法可以工作在各种不同的容器上,而无需知道这些容器的具体实现。

迭代器的分类

STL中的迭代器按照其功能和支持的操作分为五类:

  1. 输入迭代器(Input Iterator):最基础的迭代器,只支持单次遍历,只读访问元素。
  2. 输出迭代器(Output Iterator):只能写入元素,不能读取。
  3. 前向迭代器(Forward Iterator):支持多次遍历,可读写元素,只能向前移动。
  4. 双向迭代器(Bidirectional Iterator):在前向迭代器的基础上增加了向后移动的能力。
  5. 随机访问迭代器(Random Access Iterator):在双向迭代器的基础上增加了随机访问和迭代器算术的能力。

每种更高级别的迭代器都包含了前一级别迭代器的所有功能。不同的容器提供不同类型的迭代器,例如:

  • std::vector 提供随机访问迭代器
  • std::list 提供双向迭代器
  • std::forward_list 提供前向迭代器
  • std::istream_iterator 是输入迭代器
  • std::ostream_iterator 是输出迭代器

C++20新增了**连续迭代器(Contiguous Iterator)**作为随机访问迭代器的子类,对应于元素在内存中连续存储的容器(如std::vector)。

迭代器的基本操作

迭代器提供了一系列基本操作,具体取决于其类别:

所有迭代器共有的操作

cpp 复制代码
*it         // 解引用迭代器
it->member  // 访问元素的成员(相当于(*it).member)
++it        // 前置自增(移动到下一个元素)
it++        // 后置自增
it1 == it2  // 相等比较
it1 != it2  // 不等比较

双向迭代器增加的操作

cpp 复制代码
--it        // 前置自减(移动到前一个元素)
it--        // 后置自减

随机访问迭代器增加的操作

cpp 复制代码
it + n      // 迭代器向前移动n个位置
it - n      // 迭代器向后移动n个位置
it += n     // 迭代器向前移动n个位置并赋值
it -= n     // 迭代器向后移动n个位置并赋值
it1 - it2   // 计算两个迭代器之间的距离
it[n]       // 随机访问(相当于*(it + n))
it1 < it2   // 小于比较
it1 > it2   // 大于比较
it1 <= it2  // 小于等于比较
it1 >= it2  // 大于等于比较

常见STL容器的迭代器类型和用法

让我们看看各种STL容器所提供的迭代器类型以及如何使用它们:

std::vector 的迭代器

std::vector 提供了随机访问迭代器,支持所有迭代器操作。

cpp 复制代码
#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    // 获取迭代器
    auto begin = vec.begin();  // 指向第一个元素
    auto end = vec.end();      // 指向最后一个元素之后的位置
    
    // 基本遍历
    std::cout << "基本遍历: ";
    for (auto it = begin; it != end; ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    // 随机访问
    std::cout << "第三个元素: " << *(begin + 2) << std::endl;
    
    // 逆向遍历
    std::cout << "逆向遍历: ";
    for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    // 修改元素
    for (auto it = begin; it != end; ++it) {
        *it *= 2;
    }
    
    // 迭代器算术运算
    auto middle = begin + (end - begin) / 2;
    std::cout << "中间元素: " << *middle << std::endl;
    
    return 0;
}

std::list 的迭代器

std::list 提供了双向迭代器,可以向前和向后移动,但不能随机访问。

cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::list<int> lst = {1, 2, 3, 4, 5};
    
    // 正向遍历
    std::cout << "正向遍历: ";
    for (auto it = lst.begin(); it != lst.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    // 反向遍历
    std::cout << "反向遍历: ";
    for (auto it = lst.rbegin(); it != lst.rend(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    // 移动迭代器
    auto it = lst.begin();
    ++it;  // 移动到第二个元素
    ++it;  // 移动到第三个元素
    std::cout << "第三个元素: " << *it << std::endl;
    
    // 不支持随机访问,以下代码无法编译:
    // auto third = lst.begin() + 2;  // 错误!list迭代器不支持+n操作
    
    return 0;
}

std::map 的迭代器

std::map 提供的是双向迭代器,迭代时会按照键的顺序遍历。

cpp 复制代码
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<std::string, int> scores = {
        {"Alice", 95},
        {"Bob", 89},
        {"Charlie", 92},
        {"David", 88}
    };
    
    // 正向遍历
    std::cout << "学生分数:" << std::endl;
    for (auto it = scores.begin(); it != scores.end(); ++it) {
        std::cout << it->first << ": " << it->second << std::endl;
    }
    
    // 反向遍历
    std::cout << "\n按反向字母顺序:" << std::endl;
    for (auto it = scores.rbegin(); it != scores.rend(); ++it) {
        std::cout << it->first << ": " << it->second << std::endl;
    }
    
    // 查找并修改
    auto it = scores.find("Bob");
    if (it != scores.end()) {
        it->second = 91;  // 更新Bob的分数
        std::cout << "\nBob的新分数: " << it->second << std::endl;
    }
    
    return 0;
}

迭代器适配器

STL提供了几种特殊的迭代器适配器,它们构建在其他迭代器之上,提供特殊的功能:

反向迭代器

反向迭代器将遍历方向反转,通过rbegin()rend()获取。

cpp 复制代码
#include <iostream>
#include <vector>

int main() {
    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::reverse_iterator适配器
    std::reverse_iterator<std::vector<int>::iterator> rev_it(vec.end());
    std::cout << "使用反向迭代器适配器: ";
    for (; rev_it != std::reverse_iterator<std::vector<int>::iterator>(vec.begin()); ++rev_it) {
        std::cout << *rev_it << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

插入迭代器

插入迭代器允许算法将元素添加到容器中,而不是覆盖现有元素。STL提供三种插入迭代器:

  1. back_inserter - 在容器尾部插入元素
  2. front_inserter - 在容器头部插入元素
  3. inserter - 在指定位置之前插入元素
cpp 复制代码
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <iterator>

int main() {
    std::vector<int> source = {1, 2, 3, 4, 5};
    std::vector<int> dest1;
    std::list<int> dest2;
    std::vector<int> dest3 = {10, 20, 30};
    
    // back_inserter - 在尾部插入
    std::copy(source.begin(), source.end(), std::back_inserter(dest1));
    std::cout << "back_inserter结果: ";
    for (int n : dest1) std::cout << n << " ";
    std::cout << std::endl;
    
    // front_inserter - 在头部插入 (只能用于支持push_front的容器)
    std::copy(source.begin(), source.end(), std::front_inserter(dest2));
    std::cout << "front_inserter结果: ";
    for (int n : dest2) std::cout << n << " ";  // 会是倒序的
    std::cout << std::endl;
    
    // inserter - 在指定位置插入
    std::copy(source.begin(), source.end(), std::inserter(dest3, dest3.begin() + 1));
    std::cout << "inserter结果: ";
    for (int n : dest3) std::cout << n << " ";
    std::cout << std::endl;
    
    return 0;
}

流迭代器

STL还提供了连接I/O流与STL算法的流迭代器:

  1. istream_iterator - 从输入流读取元素
  2. ostream_iterator - 向输出流写入元素
cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <sstream>

int main() {
    // 读取输入流的例子
    std::istringstream iss("1 2 3 4 5");
    std::vector<int> vec;
    
    // 使用istream_iterator从字符串流读取整数
    std::istream_iterator<int> iss_iter(iss);
    std::istream_iterator<int> iss_end;  // 默认构造的迭代器表示结束
    
    std::copy(iss_iter, iss_end, std::back_inserter(vec));
    
    // 使用ostream_iterator输出到控制台
    std::cout << "从流读取的数字: ";
    std::copy(vec.begin(), vec.end(), 
              std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;
    
    // 直接使用流迭代器读取和写入
    std::istringstream input("10 20 30 40 50");
    std::cout << "数字的平方: ";
    
    std::transform(
        std::istream_iterator<int>(input),
        std::istream_iterator<int>(),
        std::ostream_iterator<int>(std::cout, " "),
        [](int x) { return x * x; }
    );
    std::cout << std::endl;
    
    return 0;
}

迭代器特性(Traits)

迭代器特性是一种用于描述迭代器属性的类模板,它使算法能够决定使用最高效的实现。C++通过std::iterator_traits来访问迭代器的特性。主要的迭代器特性包括:

  1. value_type - 迭代器指向的值的类型
  2. difference_type - 两个迭代器之间距离的类型
  3. pointer - 指向值的指针类型
  4. reference - 引用类型
  5. iterator_category - 迭代器类别
cpp 复制代码
#include <iostream>
#include <vector>
#include <list>
#include <iterator>
#include <type_traits>

template <typename Iterator>
void printIteratorCategory() {
    using Category = typename std::iterator_traits<Iterator>::iterator_category;
    
    if (std::is_same<Category, std::input_iterator_tag>::value)
        std::cout << "输入迭代器" << std::endl;
    else if (std::is_same<Category, std::output_iterator_tag>::value)
        std::cout << "输出迭代器" << std::endl;
    else if (std::is_same<Category, std::forward_iterator_tag>::value)
        std::cout << "前向迭代器" << std::endl;
    else if (std::is_same<Category, std::bidirectional_iterator_tag>::value)
        std::cout << "双向迭代器" << std::endl;
    else if (std::is_same<Category, std::random_access_iterator_tag>::value)
        std::cout << "随机访问迭代器" << std::endl;
#if defined(__cpp_lib_concepts) && __cpp_lib_concepts >= 201907L
    else if (std::is_same<Category, std::contiguous_iterator_tag>::value)
        std::cout << "连续迭代器" << std::endl;
#endif
    else
        std::cout << "未知迭代器类别" << std::endl;
}

int main() {
    std::cout << "vector迭代器: ";
    printIteratorCategory<std::vector<int>::iterator>();
    
    std::cout << "list迭代器: ";
    printIteratorCategory<std::list<int>::iterator>();
    
    std::cout << "istream_iterator: ";
    printIteratorCategory<std::istream_iterator<int>>();
    
    std::cout << "ostream_iterator: ";
    printIteratorCategory<std::ostream_iterator<int>>();
    
    // 简单的迭代器traits演示
    using VecIt = std::vector<int>::iterator;
    std::iterator_traits<VecIt>::value_type val = 10;
    std::cout << "Vector迭代器value_type: " << val << std::endl;
    
    return 0;
}

自定义迭代器

有时候,我们需要为自定义容器实现迭代器。以下是一个简单的自定义迭代器例子,用于遍历一个简单的环形缓冲区:

cpp 复制代码
#include <iostream>
#include <vector>
#include <iterator>

// 简单的环形缓冲区
template <typename T, size_t Size>
class CircularBuffer {
private:
    std::vector<T> buffer;
    size_t head;  // 开始位置
    size_t count;  // 元素数量
    
public:
    CircularBuffer() : buffer(Size), head(0), count(0) {}
    
    void push(const T& value) {
        size_t position = (head + count) % Size;
        buffer[position] = value;
        
        if (count < Size)
            ++count;
        else
            head = (head + 1) % Size;  // 环形缓冲区满了,覆盖最旧的元素
    }
    
    bool empty() const { return count == 0; }
    bool full() const { return count == Size; }
    size_t size() const { return count; }
    size_t capacity() const { return Size; }
    
    // 自定义迭代器
    class Iterator {
    private:
        CircularBuffer* buffer;
        size_t position;
        size_t visited;  // 已访问计数
    
    public:
        // 迭代器特性类型定义
        using iterator_category = std::forward_iterator_tag;
        using difference_type = std::ptrdiff_t;
        using value_type = T;
        using pointer = T*;
        using reference = T&;
        
        Iterator(CircularBuffer* buf, size_t pos, size_t vis)
            : buffer(buf), position(pos), visited(vis) {}
        
        reference operator*() const {
            return buffer->buffer[position];
        }
        
        pointer operator->() const {
            return &(buffer->buffer[position]);
        }
        
        Iterator& operator++() {
            if (visited < buffer->count) {
                position = (position + 1) % Size;
                ++visited;
            }
            return *this;
        }
        
        Iterator operator++(int) {
            Iterator temp = *this;
            ++(*this);
            return temp;
        }
        
        bool operator==(const Iterator& other) const {
            return buffer == other.buffer && 
                   (position == other.position || 
                    visited == buffer->count && other.visited == buffer->count);
        }
        
        bool operator!=(const Iterator& other) const {
            return !(*this == other);
        }
    };
    
    // 返回迭代器
    Iterator begin() {
        return Iterator(this, head, 0);
    }
    
    Iterator end() {
        return Iterator(this, (head + count) % Size, count);
    }
};

int main() {
    CircularBuffer<int, 5> cb;
    
    // 添加一些元素
    for (int i = 1; i <= 7; ++i) {
        cb.push(i);
    }
    
    std::cout << "环形缓冲区内容: ";
    for (auto it = cb.begin(); it != cb.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    // 使用范围循环
    std::cout << "使用范围循环: ";
    for (int val : cb) {
        std::cout << val << " ";
    }
    std::cout << std::endl;
    
    // 适用于STL算法
    std::cout << "使用std::for_each: ";
    std::for_each(cb.begin(), cb.end(), [](int x) {
        std::cout << x << " ";
    });
    std::cout << std::endl;
    
    return 0;
}

迭代器失效问题

使用迭代器时,需要特别注意迭代器失效的问题。当容器被修改时,指向该容器的迭代器可能会失效。不同容器的迭代器失效规则不同:

  1. vector/string

    • 插入时,如果没有内存重新分配,则只有插入点之后的迭代器失效
    • 插入时,如果发生内存重新分配,则所有迭代器都失效
    • 删除时,被删除元素之后的所有迭代器都失效
  2. deque

    • 在两端插入/删除时,所有迭代器都失效,但引用不会失效
    • 在中间插入/删除时,所有迭代器和引用都失效
  3. list/forward_list

    • 只有指向被删除元素的迭代器会失效
    • 插入不会使任何迭代器失效
  4. map/set/multimap/multiset

    • 插入时迭代器不会失效
    • 删除时,只有被删元素的迭代器失效
  5. unordered_容器

    • 插入可能导致全部迭代器失效(如果发生重新哈希)
    • 删除只会使被删元素的迭代器失效

迭代器失效示例

cpp 复制代码
#include <iostream>
#include <vector>
#include <list>

void vectorIteratorInvalidation() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    std::cout << "Vector迭代器失效示例:" << std::endl;
    
    // 错误示例:在遍历时修改容器
    std::cout << "错误示例(注释掉以防崩溃):" << std::endl;
    /*
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        if (*it == 3) {
            vec.push_back(6);  // 可能导致迭代器失效和未定义行为
        }
        std::cout << *it << " ";
    }
    */
    
    // 正确示例1:先存储元素,结束遍历后再修改
    std::cout << "正确示例1:" << std::endl;
    std::vector<int> to_add;
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        if (*it == 3) {
            to_add.push_back(6);
        }
        std::cout << *it << " ";
    }
    
    for (int val : to_add) {
        vec.push_back(val);
    }
    std::cout << "\n修改后: ";
    for (int val : vec) {
        std::cout << val << " ";
    }
    std::cout << std::endl;
    
    // 正确示例2:使用索引而非迭代器
    std::cout << "\n正确示例2:" << std::endl;
    vec = {1, 2, 3, 4, 5};
    
    for (size_t i = 0; i < vec.size(); ++i) {
        std::cout << vec[i] << " ";
        if (vec[i] == 3) {
            vec.erase(vec.begin() + i);  // 删除当前元素
            --i;  // 调整索引,避免跳过元素
        }
    }
    std::cout << "\n删除后: ";
    for (int val : vec) {
        std::cout << val << " ";
    }
    std::cout << std::endl;
}

void listIteratorInvalidation() {
    std::list<int> lst = {1, 2, 3, 4, 5};
    
    std::cout << "\nList迭代器失效示例:" << std::endl;
    
    // List在删除元素时只有被删元素的迭代器失效
    for (auto it = lst.begin(); it != lst.end(); ) {
        if (*it == 3) {
            it = lst.erase(it);  // erase返回下一个元素的迭代器
        } else {
            ++it;
        }
    }
    
    std::cout << "删除后: ";
    for (int val : lst) {
        std::cout << val << " ";
    }
    std::cout << std::endl;
}

int main() {
    vectorIteratorInvalidation();
    listIteratorInvalidation();
    
    return 0;
}

迭代器与算法的结合

STL的迭代器和算法是紧密结合的,通过迭代器,算法可以作用于任何容器。以下是一些常用算法与迭代器的结合使用:

cpp 复制代码
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <algorithm>
#include <numeric>
#include <iterator>

int main() {
    // 初始化几个容器
    std::vector<int> vec = {5, 2, 8, 1, 9, 3, 7, 6, 4};
    std::list<int> lst = {5, 2, 8, 1, 9, 3, 7, 6, 4};
    std::map<std::string, int> mp = {
        {"apple", 5},
        {"banana", 3},
        {"cherry", 7},
        {"date", 2}
    };
    
    // 排序
    std::sort(vec.begin(), vec.end());
    
    // list不支持随机访问,不能用std::sort,但有自己的sort方法
    lst.sort();
    
    // 查找
    auto vecIt = std::find(vec.begin(), vec.end(), 7);
    auto lstIt = std::find(lst.begin(), lst.end(), 7);
    auto mpIt = mp.find("cherry");
    
    std::cout << "在vector中找到7: " << (vecIt != vec.end() ? "是" : "否") << std::endl;
    std::cout << "在list中找到7: " << (lstIt != lst.end() ? "是" : "否") << std::endl;
    std::cout << "在map中找到cherry: " << (mpIt != mp.end() ? "是" : "否") << std::endl;
    
    // 计数
    int count3 = std::count(vec.begin(), vec.end(), 3);
    std::cout << "vector中3的数量: " << count3 << std::endl;
    
    // 求和
    int sum = std::accumulate(vec.begin(), vec.end(), 0);
    std::cout << "vector元素总和: " << sum << std::endl;
    
    // 转换
    std::vector<int> doubled;
    std::transform(vec.begin(), vec.end(), std::back_inserter(doubled),
                  [](int x) { return x * 2; });
    
    std::cout << "翻倍后的元素: ";
    for (int n : doubled) std::cout << n << " ";
    std::cout << std::endl;
    
    // 使用流迭代器输出
    std::cout << "使用ostream_iterator输出vector: ";
    std::copy(vec.begin(), vec.end(), 
              std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;
    
    return 0;
}

实际项目中的迭代器最佳实践

避免迭代器失效

  1. 了解每种容器的迭代器失效规则
  2. 在修改容器前保存元素或迭代器
  3. 使用容器方法返回的新迭代器(如erase返回下一个元素的迭代器)

使用更现代的方式

  1. 尽可能使用范围for循环(C++11及以上)
  2. 使用auto避免冗长的迭代器类型声明
  3. 熟悉新标准引入的迭代器工具(如C++17的std::size,C++20的std::ranges

避免无效迭代器解引用

始终检查迭代器是否有效,特别是在可能返回end()的情况下:

cpp 复制代码
auto it = container.find(key);
if (it != container.end()) {
    // 只有确认迭代器有效时才解引用
    useValue(*it);
}

使用迭代器适配器简化代码

cpp 复制代码
// 不重新定义临时容器来存储结果
std::transform(input.begin(), input.end(), 
               std::back_inserter(output),
               someTransformation);

// 直接写入流
std::copy(container.begin(), container.end(),
          std::ostream_iterator<ValueType>(std::cout, ", "));

C++20中的范围(Ranges)

C++20引入了范围库,它建立在迭代器概念之上,但提供了更便捷的接口。范围允许更简洁、更可组合的算法表达:

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>

#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L
#include <ranges>

void rangesExample() {
    std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // 使用管道语法过滤、转换和输出
    auto result = vec 
        | std::views::filter([](int n) { return n % 2 == 0; })  // 只保留偶数
        | std::views::transform([](int n) { return n * 2; });   // 将每个数乘以2
    
    std::cout << "C++20 Ranges 过滤和转换后: ";
    for (int n : result) {
        std::cout << n << " ";
    }
    std::cout << std::endl;
}
#else
void rangesExample() {
    std::cout << "您的编译器不支持C++20 Ranges" << std::endl;
}
#endif

int main() {
    rangesExample();
    return 0;
}

总结

迭代器是STL设计中最为精妙的部分之一,它实现了容器和算法的解耦,允许算法在不同的容器上以统一的方式运行。掌握迭代器对于高效使用STL至关重要。

本文讨论了迭代器的分类、操作、特性和常见问题,并展示了如何在实际项目中使用迭代器。随着C++20带来的范围库,迭代器的概念进一步演进,变得更易用和更强大。

在下一篇文章中,我们将探讨STL的第三个主要组件:算法库。我们将学习如何利用STL提供的众多算法来处理容器中的数据,从而避免重新发明轮子,编写更高效、更可靠的代码。

参考资源

  • C++ 参考手册
  • 《C++ 标准库》by Nicolai M. Josuttis
  • 《Effective STL》by Scott Meyers
  • 《The C++ Programming Language》by Bjarne Stroustrup

这是我C++学习之旅系列的第二十四篇技术文章。查看完整系列目录了解更多内容。

如有任何问题或建议,欢迎在评论区留言交流!

相关推荐
闭月之泪舞4 分钟前
《深度神经网络之数据增强、模型保存、模型调用、学习率调整》
人工智能·学习·dnn
李匠202437 分钟前
C++学习之游戏服务器开发十五QT登录器实现
服务器·c++·学习·游戏
丰锋ff1 小时前
操作系统学习笔记
笔记·学习
CodeWithMe2 小时前
【C++】vector扩容缩容
开发语言·c++
努力学习的小廉2 小时前
【C++】 —— 笔试刷题day_22
java·c++·算法
咸鱼过江3 小时前
openharmony5.0.0中C++公共基础类测试-线程相关(一)
c++·harmonyos
YuforiaCode3 小时前
第十五届蓝桥杯 2024 C/C++组 合法密码
c语言·c++·蓝桥杯
海棠蚀omo3 小时前
C++笔记-stack_queue(含deque,priority_queue,仿函数的讲解)
开发语言·c++·笔记
世事如云有卷舒4 小时前
《C++ Primer》学习笔记(四)
c++·笔记·学习