C++学习(七)(标准库+STL(iotstream公司,日期/时间,器皿,算法,迭代器,多线程))
C++ 标准模板库 (STL)
C++ 标准模板库 (STL) 是头文件的集合,提供了多种数据结构、算法和函数,以简化您的 C++ 编码体验。STL 的主要目的是通过提供一套现成的有用工具来节省时间并提高效率。STL 最常用的功能可以分为三大类:容器、算法和迭代器。
器皿
容器是 C++ 中用于数据存储和作的数据结构。它们分为四种类型:序列容器、关联容器、无序关联容器和容器适配器。
-
序列容器:这些是按顺序存储元素的线性数据结构。示例包括:
std::vector
:在运行时增长和收缩的动态数组。std::vector<int> my_vector;
std::list
: 双向链表std::list<int> my_list;
std::deque
允许在两端插入和删除的双端队列。std::deque<int> my_deque;
-
Associative Containers:这些容器以具有唯一键的排序方式存储数据。示例包括:
std::set
:按键排序的唯一元素集合。std::set<int> my_set;
std::map
:按键排序的键值对集合。std::map<std::string, int> my_map;
-
无序关联容器:这些容器使用哈希表以无序方式存储数据。示例包括:
std::unordered_set
:无特定顺序的唯一元素的集合。std::unordered_set<int> my_unordered_set;
std::unordered_map
:无特定顺序的键值对集合。std::unordered_map<std::string, int> my_unordered_map;
-
容器适配器:这些是基于其他现有容器的容器。示例包括:
std::stack
:基于 deque 或 list 的 LIFO 数据结构。std::stack<int> my_stack;
std::queue
:基于 deque 或 list 的 FIFO 数据结构。std::queue<int> my_queue;
std::priority_queue
:基于 vector 或 deque 的排序队列。std::priority_queue<int> my_priority_queue;
算法
STL 提供了几种通用算法,可用于对存储在容器中的数据执行各种作。它们分为五类:非修饰序列算法、修饰序列算法、排序算法、排序范围算法和数值算法。
一些示例包括 、 、 和 。std::find
std::replace
std::sort
std::binary_search
例如,要对向量进行排序,您可以使用以下代码:
std::vector<int> my_vec = {4, 2, 5, 1, 3};
std::sort(my_vec.begin(), my_vec.end());
迭代器
迭代器是 STL 中的一个基本概念,因为它们提供了一种访问容器中元素的统一方法。迭代器可以被认为是指针的一种高级形式。
每个容器都有自己的迭代器类型,可用于遍历元素和修改值。最常见的迭代器作是 and,用于分别获取指向容器最后一个元素的第一个和最后一个元素的迭代器。begin()
end()
例如,要遍历向量并打印其元素,可以使用以下代码:
std::vector<int> my_vec = {1, 2, 3, 4, 5};
for (auto it = my_vec.begin(); it != my_vec.end(); ++it) {std::cout << *it << " ";
}
iostream 公司
iostream
是 C++ 标准库中的一个标头,它为基本输入和输出 (I/O)作提供功能。I/O 流有助于程序与各种源 (如控制台、文件或其他程序) 之间的通信。
iostream
包括以下类:
istream
:用于来自输入源的输入作。ostream
:用于对输出目标进行输出作。iostream
:输入和输出作的 和 的组合。istream
ostream
这些类继承自基类 和 。ios
ios_base
此外,还定义了几个对象,这些对象是这些类的实例,并表示标准输入和输出流:iostream
cin
:要从标准输入中读取的对象,通常对应于键盘。istream
cout
:要写入标准输出的对象,通常是控制台。ostream
cerr
:要写入标准错误输出的对象,通常用于显示错误消息。ostream
clog
:一个对象,类似于 ,但其输出可以缓冲。ostream
cerr
以下是有关用于输入和输出作的一些代码示例:iostream
#include <iostream>int main() {int a;std::cout << "Enter a number: ";std::cin >> a;std::cout << "You entered: " << a << std::endl;return 0;
}
#include <iostream>int main() {std::cerr << "An error occurred." << std::endl;std::clog << "Logging information." << std::endl;return 0;
}
请记住在使用以下功能时包含 Header:iostream
#include <iostream>
iostream
可以理解为 C++ 中程序和外界沟通的“水管系统”。它负责让程序从外部(比如键盘、文件)接收数据,或者向外部(比如屏幕、文件)发送数据。
核心功能 🚰
-
输入流
istream
像一根“吸水的水管”,专门用来读取数据。例如:int age; std::cin >> age; // 从键盘(标准输入)读取数据到变量 age
-
输出流
ostream
像一根“喷水的水管”,专门用来输出数据。例如:std::cout << "Hello!"; // 把文字输出到屏幕(标准输出)
-
双向流
iostream
既能输入又能输出的“双向水管”(比如文件读写)。
四个常用的“水管开关” 🔘
-
std::cin
对应键盘输入,比如用户输入数字、文字:std::string name; std::cin >> name; // 用户通过键盘输入名字
-
std::cout
对应屏幕输出,用于显示正常信息:std::cout << "结果:" << 42; // 屏幕显示:结果:42
-
std::cerr
专门输出错误信息(直接显示,不缓冲),比如:if (error) {std::cerr << "程序崩溃了!"; // 立即显示错误 }
-
std::clog
输出日志信息(可能先攒着再一起显示),适合记录运行过程:std::clog << "开始处理数据..."; // 日志可能稍后显示
为什么要用 iostream
? 🤔
-
简单直观:用
<<
和>>
操作符就能输入输出,类似水流方向。 -
自动适配:能处理各种类型的数据(整数、字符串、自定义对象)。
-
错误隔离:用
cerr
单独输出错误,避免和正常信息混在一起。
举个完整例子 🛠️
#include <iostream> // 引入水管系统的工具箱int main() {int num;std::cout << "请输入一个数字:"; // 屏幕提示std::cin >> num; // 从键盘读取数字std::cout << "你输入的是:" << num; // 输出结果return 0;
}
总结:iostream
是 C++ 的“水管工”,帮你把数据从键盘、文件等地方“吸进来”,或者“喷出去”。最常用的工具是 cin
(读)、cout
(写)、cerr
(报错),记住它们就像不同用途的水龙头!🚰💻
C++ 日期时间
在 C++ 中,您可以使用该库处理日期和时间,该库是标准库 (STL) 的一部分。该库提供了各种数据类型和函数来表示和作持续时间、时间点和时钟。chrono
chrono
期间
A 表示时间跨度,可以用各种单位表示,例如秒、分钟、小时等。要创建持续时间,请使用 template 类。常见的预定义持续时间类型包括:duration
std::chrono::duration
std::chrono::seconds
std::chrono::minutes
std::chrono::hours
例:
#include <iostream>
#include <chrono>int main() {std::chrono::seconds sec(5);std::chrono::minutes min(2);std::chrono::hours hr(1);return 0;
}
std::chrono::duration
可以理解为一个“时间容器”,专门用来表示一段时间的长度,并且这个容器上贴着“单位标签”(比如秒、分钟、小时),方便你直接按需操作时间。
举个生活例子 🌰
假设你有几个瓶子,分别用来装水:
-
一个瓶子上贴着“秒”,容量是“10秒”。
-
另一个贴着“分钟”,容量是“5分钟”。
-
第三个贴着“小时”,容量是“2小时”。
这些瓶子本质上装的都是时间,但单位不同。std::chrono::duration
的作用就是帮你管理这些不同单位的时间,并且可以自动换算(比如知道“1分钟 = 60秒”)。
怎么用? 🛠️
-
直接用预定义的“时间容器”
C++ 已经帮你准备好了常见单位:#include <chrono> // 必须包含这个头文件auto sec = std::chrono::seconds(30); // 30秒的容器 auto min = std::chrono::minutes(2); // 2分钟的容器 auto hour = std::chrono::hours(1); // 1小时的容器
-
时间可以互相转换
编译器知道“1分钟=60秒”,所以可以直接运算:auto total = sec + min; // 30秒 + 2分钟 → 最终自动转成秒(30 + 120 = 150秒)
-
需要时强制转换单位
如果要将“分钟”转成“小时”,可以用duration_cast
:auto time_in_hours = std::chrono::duration_cast<std::chrono::hours>(min); // 2分钟 → 转成小时会是0小时(因为不够1小时)
为什么要设计成模板? 🤔
-
类型安全:避免你意外地把“秒”和“分钟”直接相加却不转换单位。
-
灵活扩展:你可以自己定义新单位(比如“半秒钟”或“游戏帧数”)。
实际应用场景 🎯
-
倒计时功能:用
seconds
管理剩余秒数。 -
性能测试:计算代码运行耗时(用
milliseconds
或microseconds
)。 -
游戏开发:用
duration
管理技能冷却时间或动画时长。
一个完整例子 🌟
#include <iostream>
#include <chrono>int main() {using namespace std::chrono; // 简化代码seconds timeout = 30s; // C++14 起支持字面量语法(30秒)minutes meeting_time = 2min; // 2分钟// 计算总时间(自动转成秒)auto total_seconds = timeout + meeting_time;std::cout << "总时长:" << total_seconds.count() << "秒"; // 输出:150秒// 转成小时(强制转换)hours total_hours = duration_cast<hours>(total_seconds);std::cout << "或 " << total_hours.count() << "小时"; // 输出:0小时return 0;
}
总结:std::chrono::duration
是 C++ 的时间管家,帮你把“时间长度”和“单位”打包在一起,避免单位混淆,还能自动换算。记住它就像贴了标签的容器,让时间计算既直观又安全!⏱️
时间点
A 表示特定的时间点。它通常是使用 duration 和 clock 的组合创建的。在 C++ 中,该库提供了三种 clock type:time_point
chrono
std::chrono::system_clock
:表示系统范围的实时挂钟。std::chrono::steady_clock
:表示保证永不调整的单调时钟。std::chrono::high_resolution_clock
:表示具有最短时钟周期的时钟。
例:
#include <iostream>
#include <chrono>int main() {std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();return 0;
}
时钟
时钟提供对当前时间的访问。它由以下元素组成:
time_point
:一个特定的时间点。duration
:两个时间点之间的持续时间。now()
:返回当前时间点的静态函数。
例:
#include <iostream>
#include <chrono>int main() {// Get the current time_point using system_clockstd::chrono::system_clock::time_point now = std::chrono::system_clock::now();// Get the time_point 1 hour from nowstd::chrono::system_clock::time_point one_hour_from_now = now + std::chrono::hours(1);return 0;
}
将时间点转换为日历时间
要将时间点转换为日历表示形式,您可以使用该函数。std::chrono::system_clock::to_time_t
例:
#include <iostream>
#include <chrono>
#include <ctime>int main() {std::chrono::system_clock::time_point now = std::chrono::system_clock::now();std::time_t now_c = std::chrono::system_clock::to_time_t(now);std::cout << "Current time: " << std::ctime(&now_c) << std::endl;return 0;
}
这总结了使用该库在 C++ 中处理日期和时间的基本功能。您可以在 C++ 参考中找到更高级的功能,例如强制转换持续时间和时间算术。chrono
C++ 容器
C++ 容器是标准模板库 (STL) 的一部分,它提供用于存储和组织数据的数据结构。有几种类型的容器,每种类型都有自己的特性和使用案例。在这里,我们讨论一些常用的容器:
1. 向量
向量是可以根据需要调整自身大小的动态数组。它们将元素存储在连续的内存位置,允许使用索引进行快速随机访问。
例
#include <iostream>
#include <vector>int main() {std::vector<int> vec = {1, 2, 3, 4, 5};vec.push_back(6); // Add an element to the endstd::cout << "Vector contains:";for (int x : vec) {std::cout << ' ' << x;}std::cout << std::endl;
}
2. 列表
列表是一个双向链表,它允许在恒定时间内从任何位置插入或删除元素。它不支持随机访问。对于需要经常在中间插入或删除元素的方案,列表比向量更好。
例
#include <iostream>
#include <list>int main() {std::list<int> lst = {1, 2, 3, 4, 5};lst.push_back(6); // Add an element to the endstd::cout << "List contains:";for (int x : lst) {std::cout << ' ' << x;}std::cout << std::endl;
}
3. 地图
映射是存储键值对的关联容器。它支持根据值的键检索值。默认情况下,键按升序排序。
例
#include <iostream>
#include <map>int main() {std::map<std::string, int> m;m["one"] = 1;m["two"] = 2;std::cout << "Map contains:" << std::endl;for (const auto &pair : m) {std::cout << pair.first << ": " << pair.second << std::endl;}
}
std::map
可以理解为一个“智能字典”,它用“键-值对”的方式存储数据,并且自动按“键”的顺序整理内容,让你能快速通过“键”找到对应的“值”。
🌰 举个现实例子
假设你有一个电话簿:
-
键(Key):朋友的名字(比如 "Alice"、"Bob")
-
值(Value):对应的电话号码(比如 "123456"、"789000")
std::map
的作用就是帮你把名字和号码一一对应存储,并且自动按名字的字母顺序排序,查找时非常高效。
🛠️ 核心特性
-
键值对存储
每个条目都是一个键(key) + 值(value)
的组合,类似字典的“单词 + 解释”。#include <map> std::map<std::string, int> ageBook = {{"Alice", 25},{"Bob", 30} };
-
自动排序
默认按“键”的升序(从小到大)排列。比如插入"Charlie"
后,顺序会自动调整为Alice → Bob → Charlie
。 -
唯一键
每个“键”必须是唯一的,重复插入相同键会覆盖旧值:ageBook["Alice"] = 26; // 覆盖 Alice 的年龄为 26
-
快速查找
基于“键”快速定位数据(底层用红黑树实现,时间复杂度为O(log n)
)。
🔍 常用操作
-
插入数据
ageBook.insert({"Charlie", 28}); // 插入新条目 // 或直接用 [] 运算符: ageBook["David"] = 22;
-
查找数据
if (ageBook.find("Bob") != ageBook.end()) {std::cout << "Bob的年龄是:" << ageBook["Bob"]; // 输出 30 }
-
遍历所有条目
for (const auto& entry : ageBook) {std::cout << entry.first << " 年龄:" << entry.second << std::endl; }
⚠️ 注意事项
-
键的类型必须支持比较
如果键是自定义类型(比如类对象),需要定义比较规则(如重载<
运算符)。 -
需要唯一键时用
std::map
,允许重复键用std::multimap
std::multimap<std::string, int> multiAgeBook; // 允许多个 "Alice" 条目
-
不关心顺序时可用
std::unordered_map
基于哈希表实现,查找更快(时间复杂度O(1)
),但无序。
🌟 实际应用场景
-
缓存系统(用键快速检索缓存值)
-
配置文件(如
参数名 → 参数值
) -
统计词频(单词作为键,出现次数作为值)
总结:std::map
是 C++ 的“自动整理字典”,帮你用唯一的键管理数据,且自动排序。适合需要按键快速查找或有序遍历的场景,但需注意键的唯一性和比较规则!📖🔑
4. Unordered_map
与 map 类似,无序 map 存储键值对,但它是使用哈希表实现的。这意味着与 map 相比,unordered_map具有更快的 average-case 性能,因为它不保持排序顺序。但是,最坏情况下的性能可能比 map 差。
例
#include <iostream>
#include <unordered_map>int main() {std::unordered_map<std::string, int> um;um["one"] = 1;um["two"] = 2;std::cout << "Unordered map contains:" << std::endl;for (const auto &pair : um) {std::cout << pair.first << ": " << pair.second << std::endl;}
}
std::unordered_map
可以理解为一个“快速但乱放的抽屉柜”,它用键值对存储数据,但不关心顺序,直接通过“键”的哈希值快速定位位置。它的特点是“平均速度快,但极端情况可能翻车”。
🌰 举个现实例子
假设你要管理一个快递仓库:
-
键(Key):快递单号(比如 "SF123"、"JD456")
-
值(Value):对应的包裹信息(收件人、地址等)
如果用 std::map
(有序字典),包裹会按单号顺序整齐排列,找包裹时需要逐步比对单号(类似二分查找)。
如果用 std::unordered_map
(无序哈希表),包裹会被直接扔进一个编号的柜子,找包裹时:
-
计算单号的哈希值 → 得到柜子编号。
-
直接打开对应柜子 → 快速找到包裹(如果柜子里只有1个包裹)。
但如果多个单号哈希到同一个柜子(哈希冲突),就需要在柜子里翻找所有包裹,效率降低。
🆚 与 std::map
的核心区别
特性 | std::map | std::unordered_map |
---|---|---|
底层实现 | 红黑树(平衡二叉树) | 哈希表(桶 + 链表/红黑树) |
排序 | 按键升序排列 | 完全无序 |
查找速度(平均) | O(log n)(较慢) | O(1)(极快) |
查找速度(最坏) | O(log n)(稳定) | O(n)(极慢,所有键哈希冲突) |
内存占用 | 较低(树结构紧凑) | 较高(需预留哈希桶空间) |
🛠️ 适用场景
-
用
std::unordered_map
当:
需要极速查找/插入/删除,且不关心键的顺序。
例子:缓存系统、快速检索用户ID、高频更新的键值存储。 -
用
std::map
当:
需要按键顺序遍历,或对性能稳定性要求高(避免最坏情况)。
例子:按时间顺序记录日志、需要范围查询(如找键在A~B之间的条目)。
🔍 工作原理(哈希表)
-
哈希函数:将键(如字符串)转换为一个整数(哈希值)。
// 例如哈希函数可能将 "Alice" → 42,"Bob" → 15
-
哈希桶:根据哈希值将键值对分配到不同的“桶”中。
// 假设桶数量为 10,则哈希值 42 会分配到 42 % 10 = 2 号桶
-
冲突处理:如果多个键哈希到同一个桶,用链表或红黑树存储(C++11后默认用链表,冲突多时转红黑树)。
⚠️ 注意事项
-
键必须支持哈希函数:
基本类型(int
、string
等)可以直接用,但自定义类型(如类对象)需手动实现哈希函数。struct Person {std::string name;int age; };// 自定义哈希函数 struct PersonHash {size_t operator()(const Person& p) const {return std::hash<std::string>()(p.name) ^ std::hash<int>()(p.age);} };std::unordered_map<Person, std::string, PersonHash> personMap;
-
哈希函数质量影响性能:
好的哈希函数应尽量均匀分布键,减少冲突。
📝 代码示例
#include <unordered_map>
#include <iostream>int main() {std::unordered_map<std::string, int> ageMap = {{"Alice", 25},{"Bob", 30}};// 插入新条目ageMap["Charlie"] = 28;// 查找(平均 O(1))if (ageMap.find("Bob") != ageMap.end()) {std::cout << "Bob的年龄:" << ageMap["Bob"] << std::endl; // 输出 30}// 遍历(顺序不确定!)for (const auto& entry : ageMap) {std::cout << entry.first << " : " << entry.second << std::endl;}return 0;
}
总结:
-
std::unordered_map
是“为速度牺牲顺序”的键值容器,适合高频操作且无需排序的场景。 -
选择它还是
std::map
,取决于你是否需要顺序和性能稳定性。就像选择“乱但快的仓库”还是“整齐但稍慢的书架”!📦🚀
逐句解析 📖
1. for (const auto& entry : ageMap)
-
作用:告诉编译器你要遍历
ageMap
里的每一个条目。 -
分解:
-
const auto& entry
:自动识别条目类型,用“只读引用”的方式访问(避免拷贝,高效安全)。 -
ageMap
:你要遍历的容器(比如电话簿)。
-
2. std::cout << entry.first << " : " << entry.second << std::endl;
-
作用:打印当前条目的“键”和“值”。
-
分解:
-
entry.first
:当前条目的键(比如电话簿中的名字)。 -
entry.second
:当前条目的值(比如对应的电话号码)。 -
std::endl
:换行并刷新输出。
-
🌰 现实类比
假设 ageMap
是一个纸质电话簿:
std::map<std::string, int> ageMap = {{"Alice", 25}, {"Bob", 30}, {"Charlie", 28}
};
-
循环过程:
-
打开电话簿到第一页 → 拿到 Alice 的信息(
entry.first = "Alice"
,entry.second = 25
)。 -
打印:
Alice : 25
。 -
自动翻到下一页 → 拿到 Bob 的信息。
-
打印:
Bob : 30
。 -
继续翻页,直到最后一页(Charlie)。
-
🔍 关键语法点
-
const auto& entry
-
auto
:让编译器自动推断entry
的类型(这里实际是std::pair<const std::string, int>
)。 -
const &
:避免拷贝条目,原样只读访问(高效且安全)。
-
-
基于范围的 for 循环
-
传统循环需要手动控制迭代器,容易出错:
for (auto it = ageMap.begin(); it != ageMap.end(); ++it) {std::cout << it->first << " : " << it->second << std::endl; }
-
新语法更简洁,不易出错。
-
⚠️ 注意事项
-
条目顺序:
-
如果是
std::map
,循环会按键的升序打印(Alice → Bob → Charlie)。 -
如果是
std::unordered_map
,顺序不可预测(可能是 Charlie → Alice → Bob)。
-
-
修改条目:
-
如果循环内想修改值(但不能修改键),可以去掉
const
并改用auto&
:for (auto& entry : ageMap) {entry.second += 1; // 每个人的年龄加1岁 }
-
📝 完整示例
#include <iostream>
#include <map>int main() {std::map<std::string, int> ageMap = {{"Alice", 25}, {"Bob", 30}, {"Charlie", 28}};// 遍历并打印所有条目for (const auto& entry : ageMap) {std::cout << entry.first << " 的年龄是 " << entry.second << " 岁" << std::endl;}return 0;
}
输出:
Alice 的年龄是 25 岁
Bob 的年龄是 30 岁
Charlie 的年龄是 28 岁
总结:这段代码就像让 C++ 帮你“自动翻电话簿”,逐个查看每个名字和对应的年龄,无需手动控制翻页过程!📘🔍
这些只是 C++ 容器的几个示例。还有其他容器类型,例如 、 、 、 、 和 。每个容器都有自己的用例和独特特征。了解这些容器以及何时使用它们可以大大提高您使用 C++ 的效率和有效性。set
multiset
deque
stack
queue
priority_queue
迭代器
迭代器是 C++ 标准库 () 中的对象,可帮助我们遍历数组、列表和向量等容器。从本质上讲,它们充当容器类和算法之间的桥梁。迭代器的行为类似于指针,但提供了一种更通用和抽象的方式来访问容器中的元素。STL
根据其用例,您会遇到不同类型的迭代器:
- Input Iterator:用于仅向前读取一次容器中的元素。他们不能修改元素。
例:
std::vector<int> nums = {1, 2, 3, 4};
std::istream_iterator<int> input(std::cin);
std::copy(input, std::istream_iterator<int>(), std::back_inserter(nums));
这段代码的作用是从键盘读取一串整数,动态添加到 nums
向量的末尾。可以想象成一个“自动吸管”把输入的数字源源不断吸到容器里,直到你停止输入。
代码拆解 🛠️
1. 准备容器
std::vector<int> nums = {1, 2, 3, 4}; // 初始有 1,2,3,4
-
就像准备了一个盒子(
nums
),里面已经放了数字 1,2,3,4。
2. 创建“输入吸管”
std::istream_iterator<int> input(std::cin); // 吸管对准键盘输入
-
istream_iterator
是一个“输入吸管”,专门从std::cin
(键盘)读取整数。 -
此时“吸管”处于准备状态,等待你输入数字。
3. 启动“自动吸入”程序
std::copy(input, // 开始位置:吸管准备读取std::istream_iterator<int>(), // 结束位置:吸管停止标志std::back_inserter(nums) // 目的地:把数字塞到 nums 的末尾
);
-
std::copy
:复制数据的机器。 -
input
:开始读取的位置(吸管开始工作)。 -
std::istream_iterator<int>()
:停止信号(当输入结束时,吸管停止)。 -
std::back_inserter(nums)
:自动扩展的容器接口(确保nums
能动态扩容)。
工作流程 🔄
-
你输入数字(比如
5 6 7
),按回车后: -
吸管
input
开始工作:逐个读取数字 5 → 6 → 7。 -
复制到
nums
:每读一个数字,就追加到nums
末尾。 -
何时停止?:
-
输入非整数(如字母
a
)。 -
输入结束符(Windows按
Ctrl+Z
,Mac/Linux按Ctrl+D
)。
-
最终 nums
会变成 {1,2,3,4,5,6,7}
。
类比现实场景 🥤
-
吸管 (
istream_iterator
):奶茶店的吸管,专门吸液体(这里吸整数)。 -
停止标志:吸管遇到冰块(非整数)或杯子空了(输入结束)就停止。
-
自动扩容容器 (
back_inserter
):杯子底部有弹性,吸多少液体就膨胀多大空间。
注意事项 ⚠️
-
输入必须匹配类型:如果输入字母,吸管会立即停止,后续数字不会被读取。
-
动态扩容:无需提前设置
nums
的大小,back_inserter
会自动调用push_back
。
示例运行 🖥️
假设你运行程序后输入:
10 20 30 a
(最后输入字母 a
触发停止)
结果:
nums
变为 {1,2,3,4,10,20,30}
。
- Output Iterator:用于在容器中仅向前写入元素一次。它们无法重写元素。
例:
std::vector<int> nums = {1, 2, 3, 4};
std::ostream_iterator<int> output(std::cout, ", ");
std::copy(nums.begin(), nums.end(), output);
这段代码的作用是将数组中的数字逐个输出到屏幕,并用逗号分隔,就像自动打印列表一样。以下是逐行解释:
代码拆解 🛠️
1. 准备一个数字盒子
std::vector<int> nums = {1, 2, 3, 4}; // 盒子里装了数字 1,2,3,4
-
想象你有一个盒子(
nums
),里面放了数字 1、2、3、4。
2. 连接一个“自动打印机”
std::ostream_iterator<int> output(std::cout, ", ");
// 打印机设定:用逗号分隔每个数字
-
output
是一个“自动打印机”,功能是:-
输出目标:
std::cout
(屏幕)。 -
分隔符:每次打印完一个数字后,自动加一个
", "
。
-
3. 启动“复制打印”程序
std::copy(nums.begin(), nums.end(), output);
// 把盒子里的数字逐个塞进打印机
-
std::copy
的工作流程:-
从盒子的第一个数字(
nums.begin()
)开始。 -
逐个取出数字,直到最后一个(
nums.end()
)。 -
每取一个数字,就交给
output
打印机输出。
-
输出结果 🖨️
执行后屏幕显示:
1, 2, 3, 4,
注意:最后一个数字后面会多出一个逗号(需要额外处理才能去掉)。
现实类比 🧩
-
std::copy
像一个传送带,把盒子里的物品(数字)运送到打印机。 -
ostream_iterator
是打印机,每收到一个物品就打印出来,并自动贴上分隔标签(逗号)。
改进建议 ✨
如果想避免最后的冗余逗号,可以手动处理最后一个元素,或用其他方法(如 std::join
in C++23)。不过原代码的核心目的是展示如何用迭代器简化输出。
总结:这段代码用“自动打印机”(ostream_iterator
)和传送带(std::copy
)将数组内容快速输出到屏幕,是 C++ 中高效输出容器元素的常用技巧!🖨️🚀
- Forward Iterator:类似于输入迭代器,但可用于容器中元素的多个通道。他们不能倒退。
例:
std::forward_list<int> nums = {1, 2, 3, 4};
std::forward_list<int>::iterator itr = nums.begin();
while (itr != nums.end()) {std::cout << *itr << " ";++itr;
}
这段代码的作用是遍历并打印单向链表中的所有元素。通俗解释如下:
-
创建链表:首先创建了一个名为
nums
的单向链表,里面存放数字1、2、3、4。可以想象它像一列火车,每节车厢(元素)只连接下一节。 -
获取“火车头”:用
nums.begin()
获取链表的第一个元素的位置(迭代器itr
),就像指向火车的第一节车厢。 -
遍历车厢:
-
只要
itr
没有到达“车尾后的空位置”(nums.end()
),就做两件事:-
打印当前车厢的货物:
std::cout << *itr
输出当前元素的值。 -
移动到下一节车厢:
++itr
让迭代器指向下一个元素。
-
-
-
结束条件:当处理完最后一节车厢(数字4)后,
itr
再移动就会到达“空位置”,循环停止。
最终会在屏幕上输出:1 2 3 4。整个过程就像从火车头开始,逐节检查并记录每节车厢的内容,直到所有车厢处理完毕。
反向迭代器:类似于输入迭代器,但可用于容器中元素的多个通道。他们无法前进。
例:
std::list<int> nums = {1, 2, 3, 4};
std::list<int>::reverse_iterator itr = nums.rbegin();
while (itr != nums.rend()) {std::cout << *itr << " ";++itr;
}
这段代码的作用是倒序遍历双向链表并逆序打印所有元素。通俗解释如下:
类比理解
想象一个双向链表就像一列可以前后行驶的火车,每节车厢(元素)既能看到前一节,也能看到后一节。这次我们要从最后一节车厢开始倒着走回火车头,并记录每节车厢的货物。
代码分步解释
-
创建双向链表:
std::list<int> nums = {1, 2, 3, 4};
-
创建了一个双向链表
nums
,包含数字 1、2、3、4。它的结构像这样:
头 ← 1 ↔ 2 ↔ 3 ↔ 4 ← 尾
-
-
获取“倒车起点”:
std::list<int>::reverse_iterator itr = nums.rbegin();
-
nums.rbegin()
获取链表的反向起始位置(即最后一个元素4
的位置)。
-
-
倒着遍历车厢:
while (itr != nums.rend()) {std::cout << *itr << " ";++itr; // 注意这里是 ++,但实际会向前移动 }
-
循环条件:只要迭代器还没到达“车头前的空位置”(
nums.rend()
)。 -
关键操作:
-
*itr
读取当前车厢的货物(比如先从4
开始)。 -
++itr
:看似“向前走”,但由于是反向迭代器,实际是移动到前一个元素(从4
移动到3
,再移动到2
,依此类推)。
-
-
输出结果
最终会在屏幕上输出:4 3 2 1
(相当于从火车尾倒着走回火车头,依次记录每节车厢的内容)
对比正向迭代器
-
正向迭代器 (
begin()
→end()
):从头到尾,输出1 2 3 4
。 -
**反向迭代器 (
rbegin()
→rend()
):从尾到头,输出4 3 2 1
。
反向迭代器的 ++
操作实际上是“向链表的头部方向移动”,类似于倒车时踩油门反而是在后退。
- 双向迭代器:这些迭代器提供了在容器中向前和向后移动的能力。List 和 set 容器具有双向迭代器。
例:
std::list<int> nums = {1, 2, 3, 4};
std::list<int>::iterator itr;
for (itr = nums.begin(); itr != nums.end(); ++itr) {std::cout << *itr << " ";
}
for (--itr; itr != nums.begin(); --itr) {std::cout << *itr << " ";
}
- 随机访问迭代器:这些迭代器提供了访问容器中元素的最灵活方式。它们可以向前、向后移动、直接跳转到其他元素以及访问给定索引处的元素。
例:
std::vector<int> nums = {1, 2, 3, 4};
std::vector<int>::iterator itr;
for (itr = nums.begin(); itr != nums.end(); ++itr) {std::cout << *itr << " ";
}
for (itr -= 1; itr != nums.begin() - 1; --itr) {std::cout << *itr << " ";
}
在大多数情况下,您需要从关键字和适当的容器方法(如 和 )开始,以便与迭代器一起使用。auto
begin()
end()
例:
std::vector<int> nums = {1, 2, 3, 4};
for (auto itr = nums.begin(); itr != nums.end(); ++itr) {std::cout << *itr << " ";
}
使用算法时,请记住 C++ 标准库提供了各种算法,这些算法已经将迭代器用于搜索、排序和作元素等任务。
STL 算法
C++ 中的标准模板库 (STL) 提供了一组泛型算法,这些算法旨在与各种容器类一起使用。这些算法作为函数实现,可以应用于不同的数据结构,例如数组、向量、列表等。算法的主要头文件是 .<algorithm>
关键概念
排序
排序是指按特定顺序排列元素序列。STL 提供了多种排序算法,例如 、 和 。std::sort
std::stable_sort
std::partial_sort
std::sort
std::sort
用于按非降序(默认情况下)对一系列元素 [first, last] 进行排序。您还可以使用自定义比较函数或 lambda 表达式来更改排序顺序。
例:
#include <algorithm>
#include <vector>
#include <iostream>int main() {std::vector<int> nums = {10, 9, 8, 7, 6, 5};std::sort(nums.begin(), nums.end());for (int num : nums) {std::cout << num << ' ';}// Output: 5 6 7 8 9 10
}
-
范围for循环 (
for (int num : nums)
) 是C++11引入的语法糖,底层依然通过迭代器实现,但隐藏了复杂的迭代器操作。
搜索
搜索是指查找特定元素是否存在于给定的元素范围内。STL 提供了多种搜索算法,例如 、 和 。std::find
std::binary_search
std::find_if
std::find
std::find
用于查找 [first, last] 范围内给定值首次出现的迭代器。
例:
#include <algorithm>
#include <vector>
#include <iostream>int main() {std::vector<int> nums = {5, 6, 7, 8, 9, 10};auto it = std::find(nums.begin(), nums.end(), 9);if (it != nums.end()) {std::cout << "Found 9 at position: " << (it - nums.begin());} else {std::cout << "9 not found";}// Output: Found 9 at position: 4
}
修改序列
STL 还提供了用于修改序列的算法,例如 、 和 。std::remove
std::replace
std::unique
std::remove
std::remove
用于从给定范围 [first, last) 内的容器中删除值的所有实例。请注意,该函数在删除元素后不会调整容器的大小。
例:
#include <algorithm>
#include <vector>
#include <iostream>int main() {std::vector<int> nums = {5, 6, 7, 6, 8, 6, 9, 6, 10};nums.erase(std::remove(nums.begin(), nums.end(), 6), nums.end());for (int num : nums) {std::cout << num << ' ';}// Output: 5 7 8 9 10
}
我们可以用清理房间的比喻来解释这段代码的功能:
代码作用
删除数组nums
中所有数字6,并压缩剩余元素(类似大扫除时把垃圾扔掉,再把有用的物品摆整齐)。
分步解释
-
准备房间:
std::vector<int> nums = {5, 6, 7, 6, 8, 6, 9, 6, 10};
-
想象你有一个收纳盒,里面杂乱地放着数字:
📦 [5, 6, 7, 6, 8, 6, 9, 6, 10]
-
-
标记垃圾(数字6):
std::remove(nums.begin(), nums.end(), 6)
-
这个操作像把垃圾推到收纳盒的角落:
📦 [5, 7, 8, 9, 10, ❌6, ❌6, ❌6, ❌6] -
注意:此时收纳盒大小没变,只是把垃圾集中到末尾(
std::remove
返回指向第一个垃圾的位置,图中❌
处)
-
-
彻底扔掉垃圾:
nums.erase(..., nums.end());
-
把标记为垃圾的部分整个切掉,收纳盒变小:
📦 [5, 7, 8, 9, 10]
-
-
展示整理结果:
for (int num : nums) { std::cout << num << ' '; }
-
检查收纳盒剩余物品,输出:5 7 8 9 10
-
关键技术点
-
"标记-清理"策略:先通过
std::remove
集中垃圾,再用erase
批量删除,比逐个删除效率更高。 -
保持顺序:剩余元素的原始顺序不变(类似整理时不打乱物品摆放顺序)。
类比现实场景
假设你要清理一筐混着苹果和烂苹果的水果:
-
先把好苹果推到筐的前半部分,烂苹果挤到后半部(
std::remove
) -
直接把后半筐烂苹果倒掉(
erase
)
总结
C++ 中的 STL 算法为排序、搜索和修改序列等关键作提供了一组有用的函数。这些算法可以与各种容器类一起使用,使其具有高度的通用性,并且是 C++ 编程的重要组成部分。
C++ 中的多线程
多线程是在单个进程或程序中并发执行多个线程。它通过允许并行执行多个任务来提高应用程序的性能和效率。
在 C++ 中,通过 C++11 标准中引入的库提供多线程支持。thread
基本线程创建
要创建新线程,请包含头文件并创建一个将函数作为参数的实例。该函数将在新线程中执行。<thread>
std::thread
#include <iostream>
#include <thread>void my_function() {std::cout << "This function is executing in a separate thread" << std::endl;
}int main() {std::thread t(my_function);t.join(); // waits for the thread to completereturn 0;
}
带参数的线程
您可以通过将参数作为附加参数提供给构造函数来将参数传递给线程函数。std::thread
#include <iostream>
#include <thread>void print_sum(int a, int b) {std::cout << "The sum is: " << a + b << std::endl;
}int main() {std::thread t(print_sum, 3, 5);t.join();return 0;
}
互斥锁和锁
当多个线程访问共享资源时,可能会出现数据争用。为避免这种情况,请使用 mutex 和 locks 来同步共享资源访问。
#include <iostream>
#include <mutex>
#include <thread>std::mutex mtx;void print_block(int n, char c) {{std::unique_lock<std::mutex> locker(mtx);for (int i = 0; i < n; ++i) {std::cout << c;}std::cout << std::endl;}
}int main() {std::thread t1(print_block, 50, '*');std::thread t2(print_block, 50, '$');t1.join();t2.join();return 0;
}
我们可以用银行ATM机排队的比喻来解释这段代码的功能:
代码作用
让两个窗口(线程)有序地打印各自符号,避免打印内容混合(类似ATM机每次只允许一人操作)
分步解释
-
准备两个窗口:
td::thread t1(print_block, 50, '*'); // 窗口1要打印50个* std::thread t2(print_block, 50, '$'); // 窗口2要打印50个$
-
想象银行有两个ATM机(线程),都想同时打印符号
-
-
安装门禁系统(互斥锁):
std::mutex mtx; // 相当于ATM机的门禁,一次只放一人进入
-
取号进门(加锁):
std::mutex mtx; // 相当于ATM机的门禁,一次只放一人进入
-
当某个线程执行到这里时:
-
如果门禁卡空闲 → 立即拿到卡进入(开始打印)
-
如果门禁卡被占用 → 在门口排队等待
-
-
-
完整打印(独占资源):
for (int i=0; i<50; ++i) { std::cout << c; } // 连续打印50次
-
进入ATM机的人会连续打印完所有符号后才出来(不会被中途打断)
-
-
自动还卡(解锁):
} // unique_lock在这里自动释放锁
-
打印完成后,
unique_lock
会自动归还门禁卡,让另一个窗口的人进入
-
输出结果示例
*************************
$$$$$$$$$$$$$$$$$$$$$$$$$
或者
$$$$$$$$$$$$$$$$$$$$$$$$$
*************************
(最终会完整打印两行符号,但先后顺序可能随机,取决于系统调度)
关键特性
-
原子性打印:每个线程的50个符号会完整显示,不会出现
*$*$
交替 -
自动管理锁:
unique_lock
在作用域结束后自动解锁,即使发生异常也能保证解锁 -
线程安全:避免多个线程同时操作
std::cout
导致输出混乱
对比不加锁的情况
如果去掉锁相关的代码,输出可能会变成碎片化的混合内容(类似两个ATM用户同时挤进一个机器操作):
*$**$$$*$*$*$$*$*$*...(乱序混合)
这个简短的介绍应该可以帮助您开始使用 C++ 中的基本多线程技术。还有很多内容需要学习,例如线程池、条件变量以及用于高级同步和性能调整的原子作。
相关文章:
C++学习(七)(标准库+STL(iotstream公司,日期/时间,器皿,算法,迭代器,多线程))
C 标准模板库 (STL) C 标准模板库 (STL) 是头文件的集合,提供了多种数据结构、算法和函数,以简化您的 C 编码体验。STL 的主要目的是通过提供一套现成的有用工具来节省时间并提高效率。STL 最常用的功能可…...
c高级第五天
1> 在终端提示输入一个成绩,通过shell判断该成绩的等级 [90,100] : A [80, 90) : B [70, 80) : C [60, 70) : D [0, 60) : 不及格 #!/bin/bash# 提示用户输入成绩 read -p "请输入成绩(0-100):" score# 判断成…...
Windows上使用go-ios实现iOS17自动化
前言 在Windows上运行iOS的自动化,tidevice对于iOS17以上并不支持,原因是iOS 17 引入新通信协议 RemoteXPCQUIC,改变了 XCUITest 的启动方式。 一、go-ios的安装 1、安装命令:npm i go-ios 2、安装完成后输入命令which io…...
迷你世界脚本小地图接口:Mapmark
小地图接口:Mapmark 彼得兔 更新时间: 2023-10-25 10:33:48 具体函数名及描述如下: 序号 函数名 函数描述 1 newShape(...) 新增一个形状(线,矩形,圆形) 2 deleteShape(...) 删除一个形状 3 setShapeColor(...) 设置…...
TMS320F28P550SJ9学习笔记1:CCS导入工程以及测试连接单片机仿真器
学习记录如何用 CCS导入工程以及测试连接单片机仿真器 以下为我的CCS 以及驱动库C2000ware 的版本 CCS版本: Code Composer Studio 12.8.1 C2000ware :C2000Ware_5_04_00_00 目录 CCS导入工程: 创建工程: 添加工程: C…...
为什么要提倡尽早返回(Early Return)
为什么要提倡尽早返回(Early Return) 在编程中,“尽早返回”(Early Return)是一种常被提倡的编程方式,特别是在需要提升代码可读性、减少嵌套层级、以及快速处理异常情况时。本文将讨论尽早返回的优点、应…...
Gartner发布安全运营指标构建指南
如何为安全运营指标构建坚实的基础 安全运营经理需要报告威胁检测、调查和响应计划的有效性,但难以驾驭大量潜在的 SOC 指标。本研究提供了设计针对 SOC 的指标系统的示例和实践。 主要发现 需要清晰、一致的衡量标准来向董事会成员或服务提供商等更广泛的团队传达…...
vue3:初学 vue-router 路由配置
承上一篇:nodejs:express js-mdict 作为后端,vue 3 vite 作为前端,在线查询英汉词典 安装 cnpm install vue-router -S 现在讲一讲 vue3:vue-router 路由配置 cd \js\mydict-web\src mkdir router cd router 我还…...
数据结构入门篇——什么是数据结构。
一、引入 工具是一种什么东西呢?是一种转化媒介,我们需要熟食,我们要通过用火来将生肉烤熟。在这个过程中。我们要输入一个东西——生肉,通过工具——火的加工,从而得到我们的目的产物——熟肉。 将上面的例子和红字部…...
uniapp+vue3搭建项目
工具使用: Pinia Vue 3 官方推荐的状态管理库,比 Vuex 更轻量,支持模块化,结合 persistedstate 插件可以持久化存储数据。uView-plus 专为 UniApp 设计,支持 App、小程序、H5。UnoCSS 更轻量,比 TailwindCS…...
unity大坐标抖动处理测试
第二幅图就是相机坐标是0 6360094 0的地方看见的模型,可以看见这个球体已经烂了 那么这里可以知道的是坐标太大了导致的,那么把所有物体共同偏移一下,即可得到第一幅图的效果,圆润的sphere又回来了 浮点数的计算是需要位数的&…...
CASAIM与承光电子达成深度合作,三维扫描逆向建模技术助力车灯设计与制造向数字化与智能化转型
近日,CASAIM与广州承光电子科技有限公司正式达成深度合作,CASAIM将为承光电子提供全方位的技术支持,包括高精度三维扫描设备、逆向建模软件以及定制化的技术解决方案。双方将共同组建技术团队,针对车灯设计中的难点进行攻关&#…...
C++类与对象:银行管理系统项目实战开发LeetCode每日一题
[Bank-Management-System]银行管理系统项目 以下是一个可运行的C银行账户类(支持简单的存款/取款)。后面会继续完善该项目: #include <iostream> #include <string> using namespace std;class Account{public://构造函数Accou…...
领域驱动设计:事件溯源架构简介
概述 事件溯源架构通常由3种应用设计模式组成,分别是:事件驱动(Event Driven),事件溯源(Event Source)、CQRS(读写分离)。这三种应用设计模式常见于领域驱动设计(DDD)中,但它们本身是一种应用设计的思想,不仅仅局限于DDD,每一种模式都可以单独拿出来使用。 E…...
景联文科技:以专业标注赋能AI未来,驱动智能时代的精准跃迁
在人工智能技术重塑全球产业格局的今天,高质量训练数据已成为驱动算法进化的核心燃料。作为数据智能服务领域的领军者,景联文科技深耕数据标注行业多年,以全栈式数据解决方案为核心,构建起覆盖数据采集、清洗、标注、质检及算法调…...
LeetCode 热题 100----1.两数之和
LeetCode 热题 100----1.两数之和 题目描述 我的解法 语言:js 思路就是:用双重循环去找哪两个数字相加等于target,目前的时间复杂度为O(n2),之后右优化思路再更新。...
GIT 常用命令
/ 一、环境: ssh-keygen -t rsa -C "wangxiaoerqq.com.cn" 生成本地秘钥(邮箱换成自己的邮箱) 使用cat ~/.ssh/id_rsa.pub查看秘钥 git config --global user.name "wangxiaoer" git config --global wangxiaoerqq.…...
Netty笔记13:序列化
Netty笔记1:线程模型 Netty笔记2:零拷贝 Netty笔记3:NIO编程 Netty笔记4:Epoll Netty笔记5:Netty开发实例 Netty笔记6:Netty组件 Netty笔记7:ChannelPromise通知处理 Netty笔记8…...
IntelliJ IDEA 构建项目时内存溢出问题
问题现象 在使用 IntelliJ IDEA 构建 Java 项目时,遇到了以下错误: java: java.lang.OutOfMemoryError: Java heap space java.lang.RuntimeException: java.lang.OutOfMemoryError: Java heap space这是一个典型的 Java 堆内存不足错误,表…...
Asp.Net Core WebAPI开发教程(入门)
一、Asp.Net Core WebAPI项目创建 二、Asp.Net Core WebApi/Mvc路由定义 二、Asp.Net Core WebAPI 请求案例 Asp.Net WebApi Get请求整理(一) Asp.Net WebApi Post请求整理(一) Asp.Net WebApi Action命名中已‘Get’开头问题 …...
golang 内存对齐和填充规则
内存对齐和填充规则 对齐要求:每个数据类型的起始地址必须是其大小的倍数。 int8(1字节):不需要对齐。int16(2字节):起始地址必须是2的倍数。int32(4字节):起…...
MySQL执行更新SQL流程
目录 1 redo log 2 binlog 3 Update执行逻辑 1 redo log InnoDB引擎特有日志MySQL的WAL(Writing Ahead logging)技术,预写式日志,先写日志再写磁盘当有一条记录需要更新时,InnoDB引擎就会先把记录写在redo log日志中&a…...
【时序预测】在线学习:算法选择(从线性模型到深度学习解析)
——如何为动态时序预测匹配最佳增量学习策略? 引言:在线学习的核心价值与挑战 在动态时序预测场景中(如实时交通预测、能源消耗监控),数据以流式(Streaming)形式持续生成,且潜在的…...
CISC架构
基本概念 CISC 架构是一种计算机处理器设计架构,其设计理念与 RISC 架构相对。CISC 架构强调通过使用大量功能复杂的指令来增强计算机的处理能力,试图让计算机用一条指令就能完成较为复杂的操作,以减少程序中指令的总数,提高程序…...
Vue前端开发- Vant之Card组件
业务组件是Vant的一大特点,特别是针对移动端商城开发的业务,有许多组件可以直接运用到通用商城的开发中,代码也十分简单,大大加快了应用的开发速度。 在众多的业务组件中,Card 卡片、Coupon 优惠券选择器和SubmitBar …...
React Refs:深入理解与最佳实践
React Refs:深入理解与最佳实践 引言 在React中,refs是用于访问DOM元素或组件实例的一种方式。与类组件的ref属性不同,函数组件的ref需要使用useRef钩子。正确使用refs可以大大提升React应用的性能和可维护性。本文将深入探讨React Refs的原…...
Linux——基本指令
我们今天学习Linux最基础的指令 ls 指令 语法: ls [选项] [⽬录或⽂件] 功能:对于⽬录,该命令列出该⽬录下的所有⼦⽬录与⽂件。对于⽂件,将列出⽂件名以及其他信 息。 命令中的选项,一次可以传递多个 ,…...
【MySQL系列文章】Linux环境下安装部署MySQL
前言 本次安装部署主要针对Linux环境进行安装部署操作,系统位数64 getconf LONG_BIT 64MySQL版本:v5.7.38 一、下载MySQL MySQL下载地址:MySQL :: Download MySQL Community Server (Archived Versions) 二、上传MySQL压缩包到Linuxx环境,…...
【mysql】表信息无法获取与插入数据错误
一:无法获取表信息 处理流程分析 初始问题:get_room_member_list 函数调用后,未能收到预期的群成员列表回调 Error: not all arguments converted during string formatting 127.0.0.1 - - [26/Feb/2025 11:55:32] "POST / HTTP/1.1&qu…...
七、Redis 内存管理详解:模型、优化策略(LRU/LFU、对象共享)
Redis 内存管理详解:模型、优化策略(LRU/LFU、对象共享) Redis 以高性能和低延迟著称,但作为基于内存的数据库,内存管理是其核心问题之一。本文将深入解析 Redis 的内存模型、内存优化策略(包括 LRU/LFU 机制、对象共享等),帮助开发者提高 Redis 的存储效率和性能。 1…...
如何调试Linux内核?
通过创建一个最小的根文件系统,并使用QEMU和GDB进行调试。 1.准备工作环境 确保系统上安装了所有必要的工具和依赖项。 sudo apt-get update //更新一下软件包 sudo apt-get install build-essential git libncurses-dev bison flex libssl-dev qemu-system-x…...
Pywinauto Recorder: 将Windows操作转化为Python脚本,高效简易地实现操作自动化
Pywinauto Recorder是一个强大的UI自动化工具,它能够记录用户在Windows应用程序上的操作,并将其转换为可执行的Python脚本。这意味着你可以轻松地将重复性的GUI操作自动化,提高效率并降低人为错误。 什么是Pywinauto Recorder? …...
【AI大模型】DeepSeek + Kimi 高效制作PPT实战详解
目录 一、前言 二、传统 PPT 制作问题 2.1 传统方式制作 PPT 2.2 AI 大模型辅助制作 PPT 2.3 适用场景对比分析 2.4 最佳实践与推荐 三、DeepSeek Kimi 高效制作PPT操作实践 3.1 Kimi 简介 3.2 DeepSeek Kimi 制作PPT优势 3.2.1 DeepSeek 优势 3.2.2 Kimi 制作PPT优…...
Spring统一格式返回
目录 一:统一结果返回 1:统一结果返回写法 2:String类型报错问题 解决方法 二:统一异常返回 统一异常返回写法 三:总结 同志们,今天咱来讲一讲统一格式返回啊,也是好久没有讲过统一格式返…...
【商城实战(2)】商城架构设计:从底层逻辑到技术实现
【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配…...
Netty笔记6:Netty组件
Netty笔记1:线程模型 Netty笔记2:零拷贝 Netty笔记3:NIO编程 Netty笔记4:Epoll Netty笔记5:Netty开发实例 Netty笔记6:Netty组件 Netty笔记7:ChannelPromise通知处理 Netty笔记8…...
大模型学习笔记------Llama 3模型架构简介
大模型学习笔记------Llama 3模型架构 1、整体网络结构2、主要创新点3、其他关键改进点 LLaMA(Large Language Model Meta AI)系列模型是Meta发布并开源,分别在2023年2月、2023年7月和2024年4月发布了经历了LLaMA 1、LLaMA 2和LLaMA 3模型。本文只讲相对比较成熟、性…...
DeepSeek本地接口调用(Ollama)
前言 上篇博文,我们通过Ollama搭建了本地的DeepSeek模型,本文主要是方便开发人员,如何通过代码或工具,通过API接口调用本地deepSeek模型 前文:DeepSeek-R1本地搭建_deepseek 本地部署-CSDN博客 注:本文不仅…...
C语言:51单片机 基础知识
一、单片机概述 单片机的组成及其特点 单片机是指在一块芯片上集成了CPU、ROM、RAM、定时器/计数器和多种I/O接口电路等,具有一定规模的微型计算机。 特点: 1、单片机的存储器以ROM、RAM严格分工。 2、采用面向控制的指令系统。 3、单片机的I/O口引脚通…...
DAIR-V2X-R数据集服务器下载
【官方github链接】https://github.com/ylwhxht/V2X-R 点击并登录 选择并点击下载 浏览器弹窗,右键选择复制下载链接 ------------------------------------服务器下载----------------------------------------- 登录服务器,选在要下载的文件夹复制路…...
Kali CentOs 7代理
工具v2↓ kali_IP段v2端口例子<1> kali_IP段v2端口例子<2> CentOs 7 //编辑配置文件 vi /etc/profile//在该配置文件的最后添加代理配置 export http_proxyhttp://ip:port //代理服务器ip地址和端口号 export https_proxyhttp://ip:port //代理服务器ip地址和…...
LabVIEW虚拟频谱分析仪
在电子技术快速发展的今天,频谱分析已成为信号优化与故障诊断的核心手段。传统频谱分析仪虽功能强大,但价格高昂且体积笨重,难以满足现场调试或移动场景的需求。 基于LabVIEW开发的虚拟频谱分析仪通过软件替代硬件功能,显著降低成…...
商城源码的框架
商城源码的框架通常是基于某种Web开发框架或者电子商务平台来构建的。以下是一些常见的商城源码框架: WooCommerce:基于WordPress的电子商务插件,适用于小型到中型的在线商店。 Magento:一个功能强大和灵活的开源电子商务平台&am…...
关联封号率降70%!2025最新IP隔离方案实操手册
高效运营安全防护,跨境卖家必看的风险规避指南 跨境账号管理的核心挑战:关联封号风险激增 2024年,随着全球电商平台对账号合规的审查日益严苛,“关联封号”已成为跨境卖家最头疼的问题之一。无论是同一IP登录多账号、员工操作失误…...
Qwen2-Audio系列学习笔记
模型介绍 GitHub - QwenLM/Qwen2-Audio: The official repo of Qwen2-Audio chat & pretrained large audio language model proposed by Alibaba Cloud. https://arxiv.org/pdf/2407.10759 https://zhuanlan.zhihu.com/p/712987238 We introduce the latest progress o…...
高频 SQL 50 题(基础版)_1341. 电影评分
高频 SQL 50 题(基础版)_1341. 电影评分 思路 思路 (select Users.name results from Users left join MovieRating on Users.user_id MovieRating.user_id group by(Users.name) order by count(MovieRating.movie_id) desc,Users.name asc limit 1) u…...
C#批量压缩并上载CSV数据文件到Box企业云盘
C# .NET 8实现Windows下批量压缩csv文件为zip文件,然后异步上传到box企业云服务网盘路径,实现异常处理和写入运行状态日志,参数来自ini配置文件。 C# .NET 8代码示例,包含INI配置读取、CSV文件压缩、Box上传、异步处理和日志记录…...
pyside6学习专栏(九):在PySide6中使用PySide6.QtCharts绘制6种不同的图表的示例代码
PySide6的QtCharts类支持绘制各种型状的图表,如面积区域图、饼状图、折线图、直方图、线条曲线图、离散点图等,下面的代码是采用示例数据绘制这6种图表的示例代码,并可实现动画显示效果,实际使用时参照代码中示例数据的格式将实际数据替换即可…...
阿里万相,正式开源
大家好,我是小悟。 阿里万相正式开源啦。这就像是AI界突然开启了一扇通往宝藏的大门,而且还是免费向所有人敞开的那种。 你想想看,在这个科技飞速发展的时代,AI就像是拥有神奇魔法的魔法师,不断地给我们带来各种意想…...
Golang语法特性总结
1.认识Golang代码特性 package main //1.包含main函数的文件就是一个main包--当前程序的包名// import "fmt" // import "time" import("fmt""time" )//3.同时包含多个包 4.强制代码风格:函数的 { 一定和函数名在同一行,否…...