嵌入式八股文学习——STL相关内容学习
文章目录
- map和set的区别与实现
- 1. map和set的区别
- 2. 为什么set的元素和map的key不可修改?
- map和set的实现
- 1. map的实现原理
- map的操作:
- map的特点:
- 2. set的实现原理
- set的操作:
- set的特点:
- map和set的底层原理(红黑树操作)
- 总结
- map vs set:
- map和set的适用场景:
- STL 中 `map` 与 `unordered_map` 的区别
- 1. `map` 和 `unordered_map` 的底层实现
- (1)`map` 的底层数据结构
- (2)`unordered_map` 的底层数据结构
- 2. `map` 和 `unordered_map` 的存储顺序
- 示例 1:`map` 按照键值的递增顺序存储
- 示例 2:`unordered_map` 存储无序
- 3. `map` 和 `unordered_map` 的时间复杂度
- 为什么 `unordered_map` 最坏情况可能是 `O(N)`?
- 4. `map` 和 `unordered_map` 的使用要求
- **(1)`map` 需要 `key` 支持 `<` 运算符**
- (2)`unordered_map` 需要 `key` 支持 `hash` 和 `==`
- 5. 什么时候用 `map`?什么时候用 `unordered_map`?
- 总结
- STL 中的 `allocator` 作用详解
- C++ 的内存分配与释放流程
- STL `allocator` 的作用
- SGI STL 的两级内存分配策略
- 第一级配置器(`malloc` 分配)
- **特点**
- **实现原理**
- 第二级配置器(内存池技术)
- **特点**
- **实现原理**
- `allocator` 在 STL 容器中的应用
- **1. `vector`**
- **2. `list`**
- 总结
- STL 中迭代器的作用及其区别
- **1. STL 迭代器的作用**
- **2. 为什么有指针还需要迭代器?**
- **示例:迭代器可以支持链表,而指针不行**
- **3. STL 迭代器的分类**
- **4. STL 迭代器如何删除元素?**
- **(1) `vector` 和 `deque`**
- **(2) `list`**
- **(3) `set` 和 `map`**
- **总结**
- STL 中 `resize` 和 `reserve` 的区别
- 1. **`resize`:改变容器中元素的数量(`size()`)**
- 2. **`reserve`:改变容器的容量(`capacity()`)**
- 总结对比
- 何时使用 `resize` 和 `reserve`?
map和set的区别与实现
1. map和set的区别
map和set都是C++标准库中的关联容器(Associative Containers),它们的底层实现都是红黑树(Red-Black Tree, RB-Tree)。但它们的作用不同:
- map 是键值对(key-value)的集合,key作为索引,value作为数据存储。
- set 是唯一关键字集合,其中的元素本身即为key。
| 容器 | 结构 | 是否允许重复key | 是否允许修改 | 是否支持下标 |
|---|---|---|---|---|
| map | 键值对(key-value) | 不允许 | 允许修改value,不允许修改key | 支持 |
| set | 单个关键字(key) | 不允许 | 完全不可修改 | 不支持 |
2. 为什么set的元素和map的key不可修改?
map和set的底层结构是红黑树,其特点是:
- 通过key的大小关系组织数据,确保有序性。
- 插入、删除、查找的时间复杂度为 O ( log n ) O(\log n) O(logn)。
如果允许修改key:
- 修改key可能会破坏红黑树的排序规则,导致数据结构失效。
- 需要删除原key,然后插入修改后的key,这将影响红黑树的平衡,需要额外的调整操作。
- iterator(迭代器)会失效,修改后不知道应该指向原key的位置,还是新key的位置。
因此:
- set的迭代器是const的,元素不能修改。
- map的key不能修改,但value可以修改。
map和set的实现
1. map的实现原理
map是一个基于红黑树实现的有序字典,它的结构如下:
template <typename Key, typename Value>
class map {struct Node { // 红黑树节点Key key;Value value;Node* left;Node* right;Node* parent;bool color; // 红色true, 黑色false};Node* root; // 指向根节点
};
每次插入和删除都会保证红黑树的平衡,以维持 O ( log n ) O(\log n) O(logn) 的操作复杂度。
map的操作:
std::map<std::string, int> age;
age["Alice"] = 30; // 插入或修改键"Alice"的值
age["Bob"] = 25;
age["Charlie"] = 35; // 遍历 map
for (const auto& pair : age) { std::cout << pair.first << ": " << pair.second << std::endl;
}
map的特点:
- 使用
operator[]插入时,如果key不存在,会创建一个默认值的元素(慎用)。 - 使用
find()查找更安全:if (age.find("Alice") != age.end()) {std::cout << "Alice exists." << std::endl; } - 不能修改key,但可以修改value:
age["Alice"] = 40; // 允许修改value
2. set的实现原理
set是一个存储唯一元素的红黑树,其结构如下:
template <typename Key>
class set {struct Node { // 红黑树节点Key key;Node* left;Node* right;Node* parent;bool color; // 红色true, 黑色false};Node* root; // 根节点
};
set的操作:
std::set<int> numbers;
numbers.insert(10);
numbers.insert(20);
numbers.insert(30);
numbers.insert(20); // 重复元素不会插入// 遍历 set
for (const auto& num : numbers) { std::cout << num << std::endl;
}
set的特点:
- 自动排序,元素始终按从小到大的顺序存储。
- 不能修改元素,迭代器是
const:std::set<int>::iterator it = numbers.find(20); // *it = 25; // ❌错误,set元素不可修改 - 查询方式
if (numbers.find(20) != numbers.end()) {std::cout << "20 存在" << std::endl; }
map和set的底层原理(红黑树操作)
红黑树是自平衡二叉搜索树(BST),具有以下特点:
- 每个节点是红色或黑色。
- 根节点始终是黑色。
- 红色节点的子节点必须是黑色(不能连续两个红色)。
- 从任意节点到其叶子节点,黑色节点的数量必须相同(黑高相等)。
- 插入和删除时会自动调整,以保证** O ( log n ) O(\log n) O(logn)**的查询效率。
在插入、删除、查找时,红黑树会进行旋转(Rotation) 和 重新着色(Recoloring) 以保持平衡:
- 左旋:使右子树变成左子树,调整树高。
- 右旋:使左子树变成右子树,调整树高。
- 变色:调整节点颜色,保证红黑树规则。
总结
map vs set:
| 特性 | map | set |
|---|---|---|
| 底层结构 | 红黑树 | 红黑树 |
| 存储内容 | 键值对(key-value) | 单个元素(key) |
| 是否有序 | 是(按 key 排序) | 是(按 key 排序) |
| 重复key | 否 | 否 |
| 支持修改 | 只允许修改value | 完全不允许修改 |
| 支持下标操作 | 是 (operator[]) | 否 |
| 插入复杂度 | O ( log n ) O(\log n) O(logn) | O ( log n ) O(\log n) O(logn) |
| 查找复杂度 | O ( log n ) O(\log n) O(logn) | O ( log n ) O(\log n) O(logn) |
map和set的适用场景:
- map适用于:需要键值映射(如字典、缓存)。
- set适用于:需要唯一集合(如去重、快速查找)。
STL 中 map 与 unordered_map 的区别
在 C++ STL(标准模板库)中,map 和 unordered_map 都是键值对(key-value)形式的关联容器,它们的主要区别在于底层数据结构、存储顺序、操作效率等方面。下面我们从底层实现、存储顺序、时间复杂度、使用场景等方面进行详细讲解。
1. map 和 unordered_map 的底层实现
(1)map 的底层数据结构
map的底层是 红黑树(Red-Black Tree),即平衡二叉搜索树(BST)的一种。map中的元素是按照 Key 递增(默认使用operator<) 的顺序存储的,因此遍历时的输出结果是 有序的。- 由于
map依赖于树的平衡性,它的查找、插入、删除等操作的时间复杂度是 O(log N)。
(2)unordered_map 的底层数据结构
unordered_map底层使用 哈希表(Hash Table),通常是**哈希桶(Hash Bucket)+ 链地址法(拉链法)**的结构。- 由于
unordered_map是基于 哈希值(Hash Function) 进行存储的,因此遍历时元素的顺序是无序的。 - 其查找、插入、删除的平均时间复杂度为 O(1),但最坏情况下可能退化为 O(N)(当所有元素都被哈希到同一个桶时)。
2. map 和 unordered_map 的存储顺序
| 容器名称 | 存储顺序 | 底层数据结构 | 是否有序 |
|---|---|---|---|
map | 递增顺序 | 红黑树(平衡二叉搜索树) | 有序 |
unordered_map | 无序 | 哈希表(Hash Table) | 无序 |
示例 1:map 按照键值的递增顺序存储
#include <iostream>
#include <map>int main() {std::map<int, std::string> myMap;myMap[3] = "three";myMap[1] = "one";myMap[2] = "two";std::cout << "map contents (sorted by key):" << std::endl;for (const auto& pair : myMap) {std::cout << pair.first << ": " << pair.second << std::endl;}return 0;
}
输出结果(按 key 递增排序)
map contents (sorted by key):
1: one
2: two
3: three
示例 2:unordered_map 存储无序
#include <iostream>
#include <unordered_map>int main() {std::unordered_map<int, std::string> myUnorderedMap;myUnorderedMap[3] = "three";myUnorderedMap[1] = "one";myUnorderedMap[2] = "two";std::cout << "unordered_map contents (unordered):" << std::endl;for (const auto& pair : myUnorderedMap) {std::cout << pair.first << ": " << pair.second << std::endl;}return 0;
}
可能的输出结果(无序)
unordered_map contents (unordered):
3: three
1: one
2: two
注意:每次运行
unordered_map可能得到不同的输出顺序。
3. map 和 unordered_map 的时间复杂度
| 操作 | map(红黑树) | unordered_map(哈希表) |
|---|---|---|
| 插入 | O(log N) | O(1)(平均),O(N)(最坏) |
| 删除 | O(log N) | O(1)(平均),O(N)(最坏) |
| 查找 | O(log N) | O(1)(平均),O(N)(最坏) |
| 遍历 | O(N)(按 key 递增顺序) | O(N)(无序) |
为什么 unordered_map 最坏情况可能是 O(N)?
unordered_map依赖哈希函数的质量,如果哈希函数不均匀,会导致大量冲突,所有元素可能被映射到同一个哈希桶,此时查找/插入的效率会退化为 O(N)。- 因此,选择一个良好的哈希函数(避免冲突)对
unordered_map的性能至关重要。
4. map 和 unordered_map 的使用要求
(1)map 需要 key 支持 < 运算符
map内部使用operator<进行排序,所以key类型必须支持<运算符。- 对于自定义类型,需要重载
operator<。
struct Person {std::string name;int age;// 重载 < 运算符bool operator<(const Person& other) const {return age < other.age; // 按年龄排序}
};
(2)unordered_map 需要 key 支持 hash 和 ==
unordered_map需要哈希函数来计算键值的哈希值。- 对于自定义类型,需要提供
std::hash特化 和operator==。
#include <iostream>
#include <unordered_map>struct Person {std::string name;int age;bool operator==(const Person& other) const {return name == other.name && age == other.age;}
};// 自定义哈希函数
struct PersonHash {std::size_t operator()(const Person& p) const {return std::hash<std::string>()(p.name) ^ std::hash<int>()(p.age);}
};int main() {std::unordered_map<Person, int, PersonHash> personMap;personMap[{ "Alice", 25 }] = 100;personMap[{ "Bob", 30 }] = 200;for (const auto& pair : personMap) {std::cout << pair.first.name << " -> " << pair.second << std::endl;}return 0;
}
5. 什么时候用 map?什么时候用 unordered_map?
| 需求 | 使用 map | 使用 unordered_map |
|---|---|---|
需要有序存储(如按 key 排序遍历) | ✅ 必须使用 | ❌ 不适用 |
| 高效查找(O(1)) | ❌ O(log N) | ✅ O(1)(平均) |
| 插入/删除元素效率高 | ❌ O(log N) | ✅ O(1)(平均) |
key 是自定义类型(如 struct) | ✅ 需要 operator< | ✅ 需要 std::hash |
| 数据量大,性能要求高 | ❌ 相对较慢 | ✅ 适合 |
总结
map基于红黑树(有序),适用于需要排序、范围查询的情况,时间复杂度O(log N)。unordered_map基于哈希表(无序),适用于快速查找的场景,时间复杂度O(1)(最坏O(N))。- 如果不需要排序,优先选择
unordered_map,效率更高!
STL 中的 allocator 作用详解
allocator 是 C++ STL(标准模板库)中的内存分配器,它用于封装 STL 容器的底层内存管理细节。allocator 允许 STL 容器在不直接调用 new 和 delete 的情况下高效地分配、构造、销毁和释放对象,从而提高性能和内存管理的灵活性。
C++ 的内存分配与释放流程
在 C++ 语言中,动态内存管理通常分为两个阶段:
-
new 操作
- 第一阶段:调用
::operator new()从堆中分配内存。 - 第二阶段:调用对象的 构造函数,在刚分配的内存上构造对象。
- 第一阶段:调用
-
delete 操作
- 第一阶段:调用对象的 析构函数,销毁对象内容。
- 第二阶段:调用
::operator delete()释放内存。
STL allocator 的作用
为了更加细化内存管理,STL 的 allocator 将上述两个阶段拆分成独立的函数:
-
内存分配与释放
allocate()—— 申请内存,不调用构造函数。deallocate()—— 释放内存,不调用析构函数。
-
对象的构造与析构
construct()—— 在已分配的内存上调用构造函数。destroy()—— 调用析构函数,但不释放内存。
这种拆分方式可以提高 STL 容器的灵活性,例如:
- 在
vector容器中,可以 一次性分配 大片内存,而不必为每个元素单独调用new。 - 在
list容器中,可以 自由地构造和析构元素,但不立即释放内存,提高插入/删除操作的效率。
SGI STL 的两级内存分配策略
SGI STL(即 GNU STL)为了提升内存管理效率,针对不同大小的对象采用了两种不同的内存分配策略:
-
第一级配置器(large object allocator)
- 使用
malloc()和free()直接从堆中分配和释放大块内存(一般超过 128 字节)。 - 适用于大对象的分配,适用范围广,但可能导致内存碎片化。
- 使用
-
第二级配置器(small object allocator)
- 采用 内存池(memory pool)技术,使用 空闲链表(free list) 来管理小块内存(一般 小于 128 字节)。
- 通过 预分配固定大小的内存块,减少
malloc()和free()调用次数,从而提升性能。 - 减少内存碎片,提高小对象的分配效率。
第一级配置器(malloc 分配)
特点
- 直接调用
malloc()和free()进行分配和释放。 - 适用性广,适合分配大对象。
- 由于直接向操作系统申请内存,可能导致内存碎片化,影响大块连续内存的分配效率。
实现原理
template <typename T>
class simple_allocator {
public:T* allocate(size_t n) {return static_cast<T*>(malloc(n * sizeof(T)));}void deallocate(T* p, size_t) {free(p);}
};
第二级配置器(内存池技术)
特点
- 使用内存池技术:预分配固定大小的内存块,避免频繁调用
malloc/free,提高分配效率。 - 减少内存碎片:通过维护 空闲链表,存储已释放的内存块,减少碎片化问题。
- 适用于小对象:比如 STL 容器中的
list、map节点等。
实现原理
class SecondLevelAllocator {
private:struct FreeListNode {FreeListNode* next;};FreeListNode* freeList[16]; // 维护16个不同大小的空闲链表public:void* allocate(size_t size) {if (size > 128) {return malloc(size); // 直接使用第一级分配器}int index = size / 8; // 计算分配到的空闲链表索引if (freeList[index] == nullptr) {return malloc(size); // 没有可用的内存块时,直接申请新内存}// 从空闲链表中取出一个内存块FreeListNode* block = freeList[index];freeList[index] = block->next;return block;}void deallocate(void* ptr, size_t size) {if (size > 128) {free(ptr); // 直接释放大内存return;}int index = size / 8;FreeListNode* block = static_cast<FreeListNode*>(ptr);block->next = freeList[index];freeList[index] = block; // 将内存块放回空闲链表}
};
allocator 在 STL 容器中的应用
1. vector
- 由于
vector需要动态扩展容量,allocator支持预分配 一大片内存,并逐步构造元素,提高性能。 std::vector<int>默认使用allocator<int>来管理元素。
#include <iostream>
#include <vector>
#include <memory>int main() {std::allocator<int> alloc; // 创建一个 int 类型的 allocatorint* p = alloc.allocate(5); // 分配 5 个 int 空间for (int i = 0; i < 5; ++i) {alloc.construct(p + i, i * 10); // 构造元素}for (int i = 0; i < 5; ++i) {std::cout << p[i] << " "; // 输出 0 10 20 30 40}for (int i = 0; i < 5; ++i) {alloc.destroy(p + i); // 销毁元素}alloc.deallocate(p, 5); // 释放内存
}
2. list
std::list由于每个节点的大小相对较小,通常使用第二级分配器 来减少malloc/free调用次数,提高效率。
#include <iostream>
#include <list>int main() {std::list<int> lst;lst.push_back(1);lst.push_back(2);lst.push_back(3);for (int val : lst) {std::cout << val << " "; // 输出 1 2 3}
}
总结
| 配置器类型 | 适用范围 | 内存管理方式 | 优势 | 劣势 |
|---|---|---|---|---|
| 第一级配置器 | > 128B 大对象 | 直接使用 malloc/free | 通用性强,适用范围广 | 可能导致内存碎片 |
| 第二级配置器 | ≤ 128B 小对象 | 采用 内存池 和 空闲链表 | 提高效率,减少碎片化 | 适用于小对象,管理复杂 |
STL 的 allocator 封装了底层的内存管理逻辑,并结合 两级分配器 优化了内存使用,使 STL 容器既高效又易用。在性能要求较高的场景下,自定义 allocator 甚至可以进一步优化内存管理,提高程序运行速度。
STL 中迭代器的作用及其区别
在 C++ STL(标准模板库)中,**迭代器(Iterator)**是一种用于遍历容器中元素的对象,它提供了一种统一的方式来访问容器,而无需暴露容器的底层实现。与指针类似,迭代器可以用来指向和操作容器中的元素,但它比指针更加灵活和安全。
1. STL 迭代器的作用
在 STL 中,迭代器主要用于:
- 遍历 STL 容器(如
vector、list、map、set等) - 提供统一的访问接口,使得不同的容器可以使用相同的算法
- 封装底层数据结构的访问方式,使代码更通用
- 支持多种迭代方式(前向、双向、随机访问等)
迭代器的基本用法示例:
#include <iostream>
#include <vector>int main() {std::vector<int> vec = {1, 2, 3, 4, 5};// 使用迭代器遍历 vectorstd::vector<int>::iterator it;for (it = vec.begin(); it != vec.end(); ++it) {std::cout << *it << " ";}return 0;
}
输出:
1 2 3 4 5
在上面的代码中,vector<int>::iterator 作为迭代器类型,begin() 返回指向容器第一个元素的迭代器,end() 返回超出最后一个元素的迭代器,*it 访问元素,++it 移动到下一个元素。
2. 为什么有指针还需要迭代器?
指针(Pointer)本质上是一个内存地址,可以直接操作数据,而迭代器虽然在语法上类似指针,但它封装了指针的功能,提供了更高层次的抽象。主要区别如下:
| 对比项 | 迭代器(Iterator) | 指针(Pointer) |
|---|---|---|
| 适用范围 | 适用于 STL 容器 | 适用于数组和连续内存 |
| 访问方式 | 封装底层结构,提供一致的访问方式 | 直接访问内存 |
| 操作安全性 | 受 STL 规则约束,更安全 | 可能导致野指针或非法访问 |
| 兼容性 | 适用于不同类型的容器 | 仅适用于数组或连续内存 |
| 可扩展性 | 适用于链表、树等复杂数据结构 | 仅适用于线性存储结构 |
示例:迭代器可以支持链表,而指针不行
#include <iostream>
#include <list>int main() {std::list<int> myList = {10, 20, 30, 40, 50};std::list<int>::iterator it;for (it = myList.begin(); it != myList.end(); ++it) {std::cout << *it << " ";}return 0;
}
输出:
10 20 30 40 50
在 list 中,数据在内存中是不连续的,不能像数组那样使用指针进行直接访问,因此使用 list<int>::iterator 进行遍历。
3. STL 迭代器的分类
STL 根据容器的不同特性,将迭代器分为五大类:
-
输入迭代器(Input Iterator)
- 只能从头到尾单向读取
- 适用于
istream_iterator - 只支持
++it,不支持--it
-
输出迭代器(Output Iterator)
- 只能单向写入数据
- 适用于
ostream_iterator - 只支持
++it,不支持--it
-
前向迭代器(Forward Iterator)
- 具有输入迭代器的所有功能
- 可多次读取相同元素
- 适用于
forward_list
-
双向迭代器(Bidirectional Iterator)
- 可以进行
++it和--it操作 - 适用于
list、set、map
- 可以进行
-
随机访问迭代器(Random Access Iterator)
- 具有双向迭代器的所有功能
- 还可以进行随机访问
it + n - 适用于
vector、deque
示例:双向迭代器
#include <iostream>
#include <list>int main() {std::list<int> lst = {1, 2, 3, 4, 5};std::list<int>::iterator it = lst.end();while (it != lst.begin()) {--it;std::cout << *it << " ";}return 0;
}
输出:
5 4 3 2 1
这里 list 的迭代器支持 --it,可以实现反向遍历。
4. STL 迭代器如何删除元素?
不同容器的 erase() 操作对迭代器的影响不同:
(1) vector 和 deque
- 删除某个元素后,所有后续元素的迭代器都会失效
- 解决方案:使用
erase()返回的迭代器进行操作
#include <iostream>
#include <vector>int main() {std::vector<int> vec = {1, 2, 3, 4, 5};auto it = vec.begin();while (it != vec.end()) {if (*it == 3) {it = vec.erase(it); // erase 返回下一个有效的迭代器} else {++it;}}for (int num : vec) {std::cout << num << " ";}return 0;
}
输出:
1 2 4 5
(2) list
- 由于
list是双向链表,erase()仅使当前迭代器失效,其他迭代器不会失效 - 解决方案:可以直接使用
erase()返回的迭代器
#include <iostream>
#include <list>int main() {std::list<int> lst = {1, 2, 3, 4, 5};auto it = lst.begin();while (it != lst.end()) {if (*it == 3) {it = lst.erase(it);} else {++it;}}for (int num : lst) {std::cout << num << " ";}return 0;
}
输出:
1 2 4 5
(3) set 和 map
- 只有被删除元素的迭代器会失效
- 解决方案:先获取下一个迭代器,再删除
#include <iostream>
#include <map>int main() {std::map<int, std::string> myMap = {{1, "one"}, {2, "two"}, {3, "three"}};auto it = myMap.begin();while (it != myMap.end()) {if (it->first == 2) {it = myMap.erase(it); // 获取下一个有效迭代器} else {++it;}}for (const auto& pair : myMap) {std::cout << pair.first << ": " << pair.second << "\n";}return 0;
}
输出:
1: one
3: three
总结
- STL 迭代器封装了指针,实现了更安全、灵活的访问方式
- 迭代器可以适用于不同的容器,提供统一的遍历方式
- 迭代器支持多种类型(输入、输出、双向、随机访问)
erase()在不同容器中的影响不同,需要谨慎处理
这样你对 STL 迭代器的作用和实现方式就有了更清晰的理解! 🚀
STL 中 resize 和 reserve 的区别
resize 和 reserve 都是用于 vector 容器中管理大小和容量的函数,但它们的作用有所不同:
1. resize:改变容器中元素的数量(size())
- 作用:
resize用于改变容器中实际存储的元素个数(即容器的size)。 - 使用方法:
v.resize(len); - 功能:
- 如果
len大于当前的size,则会增加元素,新增的元素会使用默认构造函数初始化(对于内置类型如int,默认值为0,对于自定义类型,则是默认构造的对象)。 - 如果
len小于当前的size,则会删除多余的元素。
- 如果
- 示例:
std::vector<int> v = {1, 2, 3}; v.resize(5); // v.size() 变为 5,新增两个元素,值为 0 v.push_back(4); // v.size() 变为 6 - 总结:
resize改变的是容器中实际存储的元素数量。
2. reserve:改变容器的容量(capacity())
- 作用:
reserve用于请求容器至少能够容纳指定数量的元素,它只改变容器的最大容量(capacity),而不改变实际元素的数量(size)。 - 使用方法:
v.reserve(len); - 功能:
- 如果
len大于当前的capacity,则会重新分配内存空间以容纳len个元素,并将现有的元素复制到新的内存位置。 reserve并不会改变容器中实际的元素个数,只是为未来的元素插入预留内存,从而减少多次内存重新分配的开销。
- 如果
- 示例:
std::vector<int> v; v.reserve(10); // 容器的 capacity 变为 10,但 size 仍然是 0 v.push_back(1); // size 变为 1 - 总结:
reserve是为容器预分配内存,防止在插入新元素时发生多次内存扩展,从而提高性能。
总结对比
| 特性 | resize(len) | reserve(len) |
|---|---|---|
| 改变 | 容器中元素的数量(size()) | 容器的容量(capacity()) |
| 新增元素 | 会新增或删除元素,填充默认值 | 不会新增元素,只是为将来的元素预分配内存 |
| 容量 | 不影响容量(capacity()) | 如果新的 len 大于当前 capacity(),则会重新分配内存 |
| 性能优化 | 不会优化内存分配,可能会触发多次内存分配 | 可以减少由于频繁插入导致的内存重新分配,提高性能 |
何时使用 resize 和 reserve?
- 使用
resize:当你需要修改容器中的实际元素数量时。例如,增加或减少容器中的元素。 - 使用
reserve:当你知道容器将要插入大量元素时,提前使用reserve预留足够的内存,可以避免多次内存分配带来的性能开销。
总之,resize 用于改变容器的元素数量,而 reserve 用于优化容器的内存分配,二者的目的和使用场景有所不同。
相关文章:
嵌入式八股文学习——STL相关内容学习
文章目录 map和set的区别与实现1. map和set的区别2. 为什么set的元素和map的key不可修改? map和set的实现1. map的实现原理map的操作:map的特点: 2. set的实现原理set的操作:set的特点: map和set的底层原理(…...
【清华大学】AIGC发展研究(3.0版)
目录 AIGC发展研究报告核心内容一、团队简介二、AI哲学三、国内外大模型四、生成式内容(一)文本生成(二)图像生成(三)音乐生成(四)视频生成 五、各行业应用六、未来展望 AIGC发展研究…...
JavaSE1.0(基础语法之运算符)
算术运算符 基础运算之加 减 乘 除 取余( - * / %) 运算符之相加( ) public static void main(String[] args) {System.out.println("Hello world!");int a 10;int b 20;int c a b;System.out.println(c);//…...
二十五、实战开发 uni-app x 项目(仿京东)- 前后端轮播图
定义了一个名为 Swiper 的Java类,用于表示一个轮播图实体。它使用了 Jakarta Persistence API (JPA) 来映射数据库表,并使用了 Lombok 库来简化代码。以下是对代码的详细讲解: 1. 包声明 package com.jd.jdmall.model; 这行代码声明了该类所在的包路径为 com.jd.jdmall.mode…...
ubuntu设置开机自动运行应用
系统版本:Ubuntu 24.04.1 LTS桌面版 按招网上的资料显示,当前版本主要的实现方式有以下两种, 方式1:通过图形界面的【启动应用程序】设置开机自启动;方式2:配置为服务实现开机自启动。 但是在我的电脑上方…...
蓝桥与力扣刷题(蓝桥 数的分解)
题目:把 2019分解成 3个各不相同的正整数之和,并且要求每个正整数都不包含数字 2 和 4,一共有多少种不同的分解方法? 注意交换 3 个整数的顺序被视为同一种方法,例如 1000100118和 1001100018 被视为同一种。 解题思…...
用ACM模式模板刷hot100
面试手撕给的模板基础上写 给的模板一般是下面这样 把while内容删除(一般刷hot100题目输入不需要同时输入几组) 第一个方法里写处理输入输出 自己再写一个方法,就是力扣里的核心代码(加上static) 第一个处理输入输…...
Java IO 流:从字节到字符再到Java 装饰者模式(Decorator Pattern),解析与应用掌握数据流动的艺术
在 Java 编程中,IO(输入输出)流是处理数据输入输出的核心工具。无论是读取文件、网络通信,还是处理用户输入,IO 流都扮演着重要角色。本文将深入探讨 Java IO 流的核心概念、分类、经典代码实例及其应用场景࿰…...
爬虫案例-爬取某站视频
文章目录 1、下载FFmpeg2、爬取代码3、效果图 1、下载FFmpeg FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。 点击下载: ffmpeg 安装并配置 FFmpeg 步骤: 1.下载 FFmpeg: 2.访问 FFmpeg 官网。 3.选择 Wi…...
nacos-未经授权创建用户漏洞
1、修改配置文件 vim application.properties# 修改配置项 nacos.core.auth.enabledtrue nacos.core.auth.enable.userAgentAuthWhitefalse2、重启nacos systemctl restart nacos3、验证 打开nacos部署服务器输入命令 curl -XPOST -d “usernametest123&passwordtest!123…...
C++:IO库
一、C IO库的架构 C标准库中的IO系统基于流(Stream)的概念,分为三层结构: 流对象(如cin, cout, fstream)流缓冲区(streambuf,负责底层数据处理)数据源/目的…...
企业级前端架构设计与实战
一、架构设计核心原则 1.1 模块化分层架构 典型目录结构: src/├── assets/ # 静态资源├── components/ # 通用组件├── pages/ # 页面模块├── services/ # API服务层├── store/ # 全局状态管理├── uti…...
从入门到精通【MySQL】 CRUD
文章目录 📕1. Create 新增✏️1.1 单行数据全列插入✏️1.2 单行数据指定列插入✏️1.3 多行数据指定列插入 📕2. Retrieve 检索✏️2.1 全列查询✏️2.2 指定列查询✏️2.3 查询字段为表达式✏️2.4 为查询结果指定别名✏️2.5 结果去重查询 …...
08_双向循环神经网络
双向网络 概念 双向循环神经网络(Bidirectional Recurrent Neural Network, BiRNN)通过同时捕捉序列的正向和反向依赖关系,增强模型对上下文的理解能力。与传统的单向网络不同,BIRNN 能够同时从过去和未来的上下文信息中学习,从而提升模型的…...
JSON数据修改的实现
JSON数据的修改 示例代码如下: using System.Collections; using System.Collections.Generic; using UnityEngine; //C#命名空间(以System开头) using System.IO; using LitJson; public class JsonChange : MonoBehaviour {// Start is called befor…...
2025年Postman的五大替代工具
虽然Postman是一个广泛使用的API测试工具,但许多用户在使用过程中会遇到各种限制和不便。因此,可能需要探索替代解决方案。本文介绍了10款强大的替代工具,它们能够有效替代Postman,成为你API测试工具箱的一部分。 什么是Postman&…...
(四)---四元数的基础知识-(定义)-(乘法)-(逆)-(退化到二维复平面)-(四元数乘法的导数)
使用四元数的原因 最重要的原因是因为传感器的角速度计得到的是三个轴的角速度, 这三个轴的角速度合成一个角速度矢量, 结果就是在微小时间内绕着这个角速度矢量方向为轴旋转一定角度. 截图来源网址四元数 | Crazepony开源四轴飞行器...
汇能感知高品质的多光谱相机VSC02UA
VSC02UA概要 VSC02UA是一款高品质的200万像素的光谱相机,适用于工业检测、农业、医疗等领域。VSC02UA 包含 1600 行1200 列有源像素阵列、片上 10 位 ADC 和图像信号处理器。它带有 USB2.0 接口,配合专门的电脑上位机软件使用,可进行图像采集…...
【SpringBoot】MorningBox小程序的完整后端接口文档
以下是「晨光宅配」小程序的完整接口文档,涵盖了所有12个表的接口。 每个接口包括请求方法、URL、请求参数、响应格式和示例 接口文档 1. 用户模块 1.1 获取用户信息 URL: /user/{userId}方法: GET请求参数: userId (路径参数): 用户ID响应格式:{"userId": 1,&qu…...
Blazor+PWA技术打造全平台音乐播放器-从音频缓存到离线播放的实践之路
开局三张图… 0.起源 主要是自己现在用的是苹果手机,虽然手机很高级,但是想听自己喜欢的歌曲确是不容易,在线app都要付费,免费的本地播放器都不太好用(收费的也不太行),基础功能都不满足。此外…...
使用LangChain开发智能问答系统
代码地址见文末 1. 项目配置 1.1 Neo4j 数据库配置 1. 安装与环境变量 解压路径:将neo4j-community-5.x.x.zip解压至D:\neo4j-community-5.x.x环境变量: NEO4J_HOME: D:\neo4j-community-5.x.xJAVA_HOME: D:\neo4j-community-5.x.x\jdk(注意:需指向 JDK 目录)Path 变量…...
Centos操作系统安装及优化
Centos操作系统安装及优化 零、环境概述 主机名 centos版本 cpu 内存 Vmware版本 ip地址 test CentOS Linux release 7.6.1810 (Core) 2C 2G 15.5.1 10.0.0.10 一、介质下载 1、7.6版本下载 CentOS7.6标准版下载链接: https://archive.kernel.org/centos-vault/7.6.1810/i…...
游戏引擎学习第177天
仓库:https://gitee.com/mrxiao_com/2d_game_4 今日计划 调试代码有时可能会非常困难,尤其是在面对那些难以发现的 bug 时。显然,调试工具是其中一个非常重要的工具,但在游戏开发中,另一个非常常见的工具就是自定义的调试工具&a…...
springCloud集成tdengine(原生和mapper方式) 其一
第一种 mapper方式,原生方式在主页看第二章 一、添加pom文件 <!-- HikariCP 连接池 --><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId></dependency><!-- TDengine 连接器--><de…...
数据结构知识点1
目录 一、时间复杂度和空间复杂度 1.1时间复杂度: 1.2空间复杂度: 二、装箱和拆箱 三、泛型 3.1泛型类的使用: 3.2泛型的上界: 3.3泛型方法: 一、时间复杂度和空间复杂度 1.1时间复杂度: 时间复杂…...
时序数据库QuestDB在Winform窗体应用
以下是QuestDB在Winform使用的代码: //初始化 private void Init() { //创建数据库对象 (用法和EF Dappper一样通过new保证线程安全) SqlSugarClient Db new SqlSugarClient(new ConnectionConfig() { ConnectionString “host10.3.5.227;port8812;usernameadmin;…...
从零开始学习 Go 语言
Go 语言(又称 Golang)是由 Google 开发的一种静态强类型、编译型、并发型编程语言。它以其简洁的语法、高效的并发支持和强大的标准库而闻名,非常适合开发高性能的服务器端应用、分布式系统和云计算工具。本文将从零开始,详细介绍…...
自由学习记录(45)
顶点片元着色器(important) 1.需要在Pass渲染通道中编写着色器逻辑 2.可以使用cG或HLSL两种shader语言去编写Shader逻辑 3.代码量较多,灵活性较强,性能消耗更可控,可以实现更多渲染细节 4.适用于光照处理较少…...
数据源支持远程Excel/CSV,数据集支持分组字段功能,DataEase开源BI工具v2.10.6 LTS版本发布
2025年3月17日,人人可用的开源BI工具DataEase正式发布v2.10.6 LTS版本。 这一版本的功能变动包括:数据源方面,新增支持远程Excel/CSV数据源,支持以HTTP、HTTPS、FTP协议获取远程服务器上的Excel和CSV数据文件,并且可以…...
SpringBoot3使用CompletableFuture时java.util.ConcurrentModificationException异常解决方案
问题描述 在Spring Boot 3项目中,使用CompletableFuture进行异步编程时,偶发{"code":500,"msg":"java.util.ConcurrentModificationException"}异常,但代码中并未直接操作List或CopyOnWriteArrayList等集合类…...
