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)。

迭代器的基本操作

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

所有迭代器共有的操作

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

双向迭代器增加的操作

--it        // 前置自减(移动到前一个元素)
it--        // 后置自减

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

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 提供了随机访问迭代器,支持所有迭代器操作。

#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 提供了双向迭代器,可以向前和向后移动,但不能随机访问。

#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 提供的是双向迭代器,迭代时会按照键的顺序遍历。

#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()获取。

#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 - 在指定位置之前插入元素
#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 - 向输出流写入元素
#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 - 迭代器类别
#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 >= 201907Lelse if (std::is_same<Category, std::contiguous_iterator_tag>::value)std::cout << "连续迭代器" << std::endl;
#endifelsestd::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;
}

自定义迭代器

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

#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;elsehead = (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_容器

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

迭代器失效示例

#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的迭代器和算法是紧密结合的,通过迭代器,算法可以作用于任何容器。以下是一些常用算法与迭代器的结合使用:

#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()的情况下:

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

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

// 不重新定义临时容器来存储结果
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引入了范围库,它建立在迭代器概念之上,但提供了更便捷的接口。范围允许更简洁、更可组合的算法表达:

#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; });   // 将每个数乘以2std::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;
}
#endifint 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++学习之旅系列的第二十四篇技术文章。查看完整系列目录了解更多内容。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/78060.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode刷题——判断对称二叉树(C语言版)

题目描述&#xff1a; 示例 1&#xff1a; 输入&#xff1a;root [6,7,7,8,9,9,8] 输出&#xff1a;true 解释&#xff1a;从图中可看出树是轴对称的。 示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false 解释&#xff1a;从图中可看出最…

无法右键下载文档?网页PDF下载方法大全

适用场景&#xff1a;绕过付费限制/无法右键下载/动态加载PDF 方法1&#xff1a;浏览器原生下载&#xff08;成功率60%&#xff09; Chrome/Edge&#xff1a; 在PDF预览页点击工具栏 ⬇️下载图标&#xff08;右上角&#xff09; 快捷键&#xff1a;CtrlS → 保存类型选PDF …

基于缺失数据的2024年山东省专项债发行报告

一、数据情况 本次报告选取了山东省财政局公开的2024年专项债数据,共计2723条,发行期数是从第1期到第58期,由于网络原因,其中25期到32期,54到57期的数据有缺失,如下图所示。 从上图看出,一年52周,平均每周都有一期发布,因此持续做专项债的谋划很重要,一定要持续谋划…

Ubuntu数据连接访问崩溃问题

目录 一、分析问题 1、崩溃问题本地调试gdb调试&#xff1a; 二、解决问题 1. 停止 MySQL 服务 2. 卸载 MySQL 相关包 3. 删除 MySQL 数据目录 4. 清理依赖和缓存 5.重新安装mysql数据库 6.创建程序需要的数据库 三、验证 1、动态库更新了 2、头文件更新了 3、重新…

Linux系统编程 day10 接着线程(中期头大,还要写论文)

线程有点懵逼 线程之前函数回顾以及总结部分&#xff08;对不清楚的问题再思考&#xff09; 线程控制原语 进程控制原语 pthread_create(); fork(); pthread_self(); getpid(); pthread_exit(); exit(); pthread_join(); …

《浔川AI翻译v6.1.0问题已修复公告》

《浔川AI翻译v6.1.0问题已修复公告》 尊敬的浔川AI翻译用户&#xff1a; 感谢您对浔川AI翻译的支持与反馈&#xff01;我们已针对 **v6.1.0** 版本中用户反馈的多个问题进行了全面修复&#xff0c;并优化了系统稳定性。以下是本次修复的主要内容&#xff1a; 已修复问题 ✅…

深入理解 java synchronized 关键字

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

华三(H3C)与华为(Huawei)设备配置IPsec VPN的详细说明,涵盖配置流程、参数设置及常见问题处理

以下是针对华三&#xff08;H3C&#xff09;与华为&#xff08;Huawei&#xff09;设备配置IPsec VPN的详细说明&#xff0c;涵盖配置流程、参数设置及常见问题处理&#xff1a; 一、华三&#xff08;H3C&#xff09;设备IPsec VPN配置详解 1. 配置流程 华三IPsec VPN配置主要…

KBEngine 源代码分析(一):pyscript 目录文件介绍

pyscript 目录文件 pyscript 目录提供了 KBEngine 把 C++ 代码中的类注册到 Python 的机制 同时也提供了 C++ 调用 Python 方法的例子 相对现在的 C++ 17/20 ,这个目录的分装相对不优雅 不过不影响学习如何使用 Python 官方库提供的 API ,实现 C++ Python 混合编程 C++ …

线程入门3

synchronized修饰方法 synchronized可以修饰代码块(在线程入门2中有例子)&#xff0c;也可以修饰普通方法和静态方法。 修饰普通方法 修饰普通方法简化写法&#xff1a; 修饰静态方法 修饰静态方法简化写法&#xff1a; 注意&#xff1a;利用synchronized上锁&#xff0c;锁的…

linux上Flexlm命令

FlexLM 是一种灵活的许可证管理系统&#xff0c;广泛用于各种软件产品中&#xff0c;如 Autodesk 的 AutoCAD 和 Autodesk 的其他产品。它允许软件开发商控制软件的使用和分发&#xff0c;同时提供灵活的许可证管理策略。在 Linux 系统中使用 FlexLM 通常涉及到几个关键步骤&am…

【Java学习方法】终止循环的关键字

终止循环的关键字 一、break 作用&#xff1a;跳出最近的循环&#xff08;直接结束离break最近的那层循环&#xff09; 使用场景&#xff1a;一般搭配if条件判断&#xff0c;如果满足某个条件&#xff0c;就结束循环&#xff0c;&#xff08;场景&#xff1a;常见于暴力枚举中…

【论文精读】Reformer:高效Transformer如何突破长序列处理瓶颈?

目录 一、引言&#xff1a;当Transformer遇到长序列瓶颈二、核心技术解析&#xff1a;从暴力计算到智能优化1. 局部敏感哈希注意力&#xff08;LSH Attention&#xff09;&#xff1a;用“聚类筛选”替代“全量计算”关键步骤&#xff1a;数学优化&#xff1a; 2. 可逆残差网络…

关于在Springboot中设置时间格式问题

目录 1-设置全局时间格式1.Date类型的时间2.JDK8时间3.使Date类和JDK8时间类统统格式化时间 2-关于DateTimeFormat注解 1-设置全局时间格式 1.Date类型的时间 对于老项目来说&#xff0c;springboot中许多类使用的是Date类型的时间&#xff0c;没有用到LocalDateTime等JDK8时…

面试篇:Java并发与多线程

基础概念 什么是线程&#xff1f;线程和进程的区别是什么&#xff1f; 线程 是程序执行的最小单位&#xff0c;它是 CPU 调度和执行的基本单元。一个进程可以包含多个线程&#xff0c;这些线程共享进程的资源&#xff08;如内存&#xff09;&#xff0c;但每个线程有自己的栈…

【Qt/C++】QPrinter关于QInternal::Printer的解析

1. 问题分析 QInternal::Printer在Qt框架中并不是一个直接暴露给用户的API。相反&#xff0c;它是一个枚举值&#xff0c;用于标识QPaintDevice的类型。在Qt中&#xff0c;QPaintDevice是一个抽象类&#xff0c;用于任何可以进行绘制的设备&#xff0c;如窗口、图像、打印机等…

uniapp返回上一页接口数据更新了,页面未更新

注意&#xff1a;不是组件套组件可以不使用setTimeout延时 返回上一页一般会走onshow&#xff0c;但是接口更新了页面未更新 onShow(() > {// 切换城市后重新调用数据if (areaId.value) {const timer setTimeout(async () > {timer && clearTimeout(timer);…

MCU开发学习记录11 - ADC学习与实践(HAL库) - 单通道ADC采集、多通道ADC采集、定时器触发连续ADC采集 - STM32CubeMX

名词解释&#xff1a; ADC&#xff1a; Analog-to-Digital SAR&#xff1a;Successive Approximation Register 本文将介绍ADC的概念、相关函数以及STM32CubeMX生成ADC的配置函数。针对于ADC实践&#xff1a;单通道采集芯片内部温度传感器&#xff08;ADC1_ch16&#xff09;&a…

68元撬动未来:明远智睿2351开发板重塑嵌入式开发生态

在嵌入式开发领域&#xff0c;价格与性能的矛盾始终存在&#xff1a;高端开发板功能强大但成本高昂&#xff0c;低价产品则往往受限于性能与扩展性。明远智睿2351开发板以68元&#xff08;含税&#xff09;的定价打破这一僵局&#xff0c;通过四核1.4G处理器、全功能Linux系统与…

关于ubuntu密码正确但是无法登录的情况

参考这个文章&#xff1a; https://blog.csdn.net/cuichongxin/article/details/117462494 检查一下是不是用户被lock了 输入passwd -s username 如果用户是L状态&#xff0c;那么就是lock了。 使用 passwd -u username 解锁 关于 .bashrc 不生效 有几点&#xff1a; ~/.…