当前位置: 首页 > news >正文

嵌入式八股文学习——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

  1. 修改key可能会破坏红黑树的排序规则,导致数据结构失效。
  2. 需要删除原key,然后插入修改后的key,这将影响红黑树的平衡,需要额外的调整操作。
  3. 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的特点:
  1. 使用operator[]插入时,如果key不存在,会创建一个默认值的元素(慎用)。
  2. 使用find()查找更安全
    if (age.find("Alice") != age.end()) {
        std::cout << "Alice exists." << std::endl;
    }
    
  3. 不能修改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的特点:
  1. 自动排序,元素始终按从小到大的顺序存储。
  2. 不能修改元素,迭代器是const
    std::set<int>::iterator it = numbers.find(20);
    // *it = 25;  // ❌错误,set元素不可修改
    
  3. 查询方式
    if (numbers.find(20) != numbers.end()) {
        std::cout << "20 存在" << std::endl;
    }
    

map和set的底层原理(红黑树操作)

红黑树是自平衡二叉搜索树(BST),具有以下特点:

  1. 每个节点红色或黑色
  2. 根节点始终是黑色
  3. 红色节点的子节点必须是黑色(不能连续两个红色)。
  4. 从任意节点到其叶子节点,黑色节点的数量必须相同(黑高相等)。
  5. 插入和删除时会自动调整,以保证** O ( log ⁡ n ) O(\log n) O(logn)**的查询效率。

插入、删除、查找时,红黑树会进行旋转(Rotation)重新着色(Recoloring) 以保持平衡:

  • 左旋:使右子树变成左子树,调整树高。
  • 右旋:使左子树变成右子树,调整树高。
  • 变色:调整节点颜色,保证红黑树规则。

总结

map vs set:

特性mapset
底层结构红黑树红黑树
存储内容键值对(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 中 mapunordered_map 的区别

在 C++ STL(标准模板库)中,mapunordered_map 都是键值对(key-value)形式的关联容器,它们的主要区别在于底层数据结构、存储顺序、操作效率等方面。下面我们从底层实现、存储顺序、时间复杂度、使用场景等方面进行详细讲解。


1. mapunordered_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. mapunordered_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. mapunordered_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. mapunordered_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 容器在不直接调用 newdelete 的情况下高效地分配、构造、销毁和释放对象,从而提高性能和内存管理的灵活性。


C++ 的内存分配与释放流程

在 C++ 语言中,动态内存管理通常分为两个阶段:

  1. new 操作

    • 第一阶段:调用 ::operator new() 从堆中分配内存。
    • 第二阶段:调用对象的 构造函数,在刚分配的内存上构造对象。
  2. delete 操作

    • 第一阶段:调用对象的 析构函数,销毁对象内容。
    • 第二阶段:调用 ::operator delete() 释放内存。

STL allocator 的作用

为了更加细化内存管理,STL 的 allocator 将上述两个阶段拆分成独立的函数:

  • 内存分配与释放

    • allocate() —— 申请内存,不调用构造函数。
    • deallocate() —— 释放内存,不调用析构函数。
  • 对象的构造与析构

    • construct() —— 在已分配的内存上调用构造函数。
    • destroy() —— 调用析构函数,但不释放内存。

这种拆分方式可以提高 STL 容器的灵活性,例如:

  • vector 容器中,可以 一次性分配 大片内存,而不必为每个元素单独调用 new
  • list 容器中,可以 自由地构造和析构元素,但不立即释放内存,提高插入/删除操作的效率。

SGI STL 的两级内存分配策略

SGI STL(即 GNU STL)为了提升内存管理效率,针对不同大小的对象采用了两种不同的内存分配策略

  1. 第一级配置器(large object allocator)

    • 使用 malloc()free() 直接从堆中分配和释放大块内存(一般超过 128 字节)。
    • 适用于大对象的分配,适用范围广,但可能导致内存碎片化
  2. 第二级配置器(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 容器中的 listmap 节点等。
实现原理
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 类型的 allocator
    int* 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 容器(如 vectorlistmapset 等)
  • 提供统一的访问接口,使得不同的容器可以使用相同的算法
  • 封装底层数据结构的访问方式,使代码更通用
  • 支持多种迭代方式(前向、双向、随机访问等)

迭代器的基本用法示例:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 使用迭代器遍历 vector
    std::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 根据容器的不同特性,将迭代器分为五大类:

  1. 输入迭代器(Input Iterator)

    • 只能从头到尾单向读取
    • 适用于 istream_iterator
    • 只支持 ++it,不支持 --it
  2. 输出迭代器(Output Iterator)

    • 只能单向写入数据
    • 适用于 ostream_iterator
    • 只支持 ++it,不支持 --it
  3. 前向迭代器(Forward Iterator)

    • 具有输入迭代器的所有功能
    • 可多次读取相同元素
    • 适用于 forward_list
  4. 双向迭代器(Bidirectional Iterator)

    • 可以进行 ++it--it 操作
    • 适用于 listsetmap
  5. 随机访问迭代器(Random Access Iterator)

    • 具有双向迭代器的所有功能
    • 还可以进行随机访问 it + n
    • 适用于 vectordeque

示例:双向迭代器

#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) vectordeque

  • 删除某个元素后,所有后续元素的迭代器都会失效
  • 解决方案:使用 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) setmap

  • 只有被删除元素的迭代器会失效
  • 解决方案:先获取下一个迭代器,再删除
#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 中 resizereserve 的区别

resizereserve 都是用于 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(),则会重新分配内存
性能优化不会优化内存分配,可能会触发多次内存分配可以减少由于频繁插入导致的内存重新分配,提高性能

何时使用 resizereserve

  • 使用 resize:当你需要修改容器中的实际元素数量时。例如,增加或减少容器中的元素。
  • 使用 reserve:当你知道容器将要插入大量元素时,提前使用 reserve 预留足够的内存,可以避免多次内存分配带来的性能开销。

总之,resize 用于改变容器的元素数量,而 reserve 用于优化容器的内存分配,二者的目的和使用场景有所不同。

相关文章:

  • 测试专项4:AI算法测试在测试行业中,该如何定位自己自述
  • 地理编码/经纬度解析/经纬度地址转换接口如何用JAVA对接
  • ui_auto_study(持续更新)
  • 当今前沿科技:改变世界的最新技术趋势
  • 【Spring】深入理解 Spring 事务管理
  • VScode
  • Java 中的多线程:核心概念与应用场景
  • 机器学习——KNN数据均一化
  • Qt文件管理系统
  • Spring AI相关的面试题
  • 算法如何测试,如果数据量很大怎么办?
  • 逆波兰表达式
  • [Lc17_多源 BFS_最短路] 矩阵 | 飞地的数量 | 地图中的最高点 | 地图分析
  • 串口接收不到数据,串口RX配置(f407),f103和f407的区别
  • Linux第二章第三章练习
  • python总结
  • 微调这件小事:训练集中的输入数据该作为instruction还是input?从LLaMA-Factory的源码中寻找答案吧~
  • 深度学习框架PyTorch——从入门到精通(8)保存并加载模型
  • 渐进式滑坡多场信息演化特征与数据挖掘研究
  • 蓝桥杯C++基础算法-0-1背包(优化为一维)
  • 乌拉圭前总统何塞·穆希卡去世
  • 国务院关税税则委:调整对原产于美国的进口商品加征关税措施
  • 上海能源科技发展有限公司原董事长李海瑜一审获刑13年
  • 重庆一高校75万采购市价299元产品?工作人员:正在处理
  • 邯郸一酒店办婚宴发生火灾,新郎母亲:饭没吃成酒店还要收费
  • 交涉之政、交涉之学与交涉文献——《近代中外交涉史料丛书》第二辑“总序”