C++ - STL vector.reserve()&.push_back()

文章讲述了std::vector在push_back元素后自动扩容机制,以及在使用过程中可能出现的问题,特别是当改变迭代器设置顺序时的内存访问错误。同时介绍了reserve函数用于预设容量以避免频繁扩容,提高性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

std::vector::push_back

Add element at the end Adds a new element at the end of the vector, after its current last element. The content of val is copied (or moved) to the new element. This effectively increases the container size by one, which causes an automatic reallocation of the allocated storage space if -and only if- the new vector size surpasses the current vector capacity.

std::vector::capacity

Notice that this capacity does not suppose a limit on the size of the vector. When this capacity is exhausted and more is needed, it is automatically expanded by the container (reallocating it storage space).
The theoretical limit on the size of a vector is given by member max_size. The capacity of a vector can be explicitly altered by calling member vector::reserve.

#include <iostream>
#include <vector>

#define MAX_INT 8

int main() {
    std::vector<int> vec;
    for(int i = 1; i <= MAX_INT; ++i) {
        vec.push_back(i);
    }
    vec.push_back(666);
    std::vector<int>::iterator iter = vec.begin();
    for(iter; iter != vec.end(); ++iter) {
        std::cout << *iter << ' ';
    }
    return 0;
}

output:

1 2 3 4 5 6 7 8 666

下面是出现问题的代码,当我们颠倒上面程序中如下两条语句的顺序时就会在1 2 3 4 5 6 7 8 666之前输出一段乱码

    std::vector<int>::iterator iter = vec.begin();
    vec.push_back(666);

output:

-17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 1280319716 805348782 1 2 3 4 5 6 7 8 666

问题原因:
vector是随着增加元素而动态更改容器大小。在for循环结束后,容器内的元素个数为8,且在内存中vector的动态变化的规则是2->4->8->16。而加入第九个元素666后,容器大小被更新为16,地址也被重新指向一个新的内存段落。
因此当迭代器被设定为未更新前的容器的起始位置,便会导致指向错误。

解决方法:

std::vector::reserve

void reserve (size_type n);

Request a change in capacity
Requests that the vector capacity be at least enough to contain n elements.
If n is greater than the current vector capacity, the function causes the container to reallocate its storage increasing its capacity to n (or greater).
In all other cases, the function call does not cause a reallocation and the vector capacity is not affected.
This function has no effect on the vector size and cannot alter its elements.

频繁使用std::vector的push_back操作前最好使用reserve预留出相应内存,这样可以减小动态重分配内存所占用的时间,从而增加运行速度。

#include <iostream>
#include <vector>

#define MAX_INT 8

int main() {
    std::cout << "\nvecA:\n";
    std::vector<int> vecA;
    std::cout << "vecA.capacity: " << vecA.capacity() << std::endl;
    for(int i = 1; i <= MAX_INT; ++i) {
        vecA.push_back(i);
    }
    std::vector<int>::iterator iterA = vecA.begin();
    vecA.push_back(666);
    for(iterA; iterA != vecA.end(); ++iterA) {
        std::cout << *iterA << ' ';
    }

    std::cout << "\nvecB:\n";
    std::vector<int> vecB;
    vecB.reserve(9);
    std::cout << "vecB.capacity: " << vecB.capacity() << std::endl;
    for(int i = 1; i <= MAX_INT; ++i) {
        vecB.push_back(i);
    }
    std::vector<int>::iterator iterB = vecB.begin();
    vecA.push_back(666);
    for(iterB; iterB != vecB.end(); ++iterB) {
        std::cout << *iterB << ' ';
    }
    return 0;
}

output:

vecA:
vecA.capacity: 0
-17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -17891602 -379745926 805325510 1 2 3 4 5 6 7 8 666
vecB:
vecB.capacity: 9
1 2 3 4 5 6 7 8

注:

使用reserve指定vector的容量为n,当push_back的元素数量大于n的时候,会重新分配一个大小为2n的新空间(不跟据2的指数变化,而是在原有大小n的基础上变为2n),再将原有的n的元素和新的元素放入新开辟的内存空间中。
(注:重新分配内存,并不会在原有的地址之后紧跟着分配的新的空间,一般会重新开辟一段更大的空间,再将原来的数据和新的数据放入新的空间)

### C++ `vector` 的 `reserve` 方法详解 #### 定义与作用 `std::vector&lt;T&gt;::reserve` 是标准模板库(STL)中用于预先分配内存的方法。通过调用此函数可以预留指定数量的空间给向量对象,从而减少因动态增长而导致的多次重新分配操作。这有助于提高性能并优化程序运行效率[^1]。 #### 函数原型 ```cpp void reserve(size_type new_cap); ``` 参数说明: - `new_cap`: 需要预留给容器内部数组的新容量大小;如果当前实际存储的数据数目大于等于这个值,则不会改变原有空间大小。 返回值:无。 注意点: 当设置了一个更大的容量之后,在后续插入元素时只要不超过已设定的最大限度就不会触发额外的扩容动作。但如果超过了预定界限还是会发生自动扩展行为来容纳更多数据项。 #### 使用案例分析 ##### 示例一:基本应用实例 下面展示一段简单的代码片段用来解释如何利用 `reserve()` 来提升性能: ```cpp #include &lt;iostream&gt; #include &lt;vector&gt; int main() { const int size = 100; // 创建一个空的整型向量,并提前为其保留足够的空间 std::vector&lt;int&gt; vec; vec.reserve(size); for(int i=0;i&lt;size;++i){ vec.push_back(i*2); } return 0; } ``` 在这个例子中,创建了一个名为 `vec` 的空向量,并立即使用 `reserve()` 设置其初始容量为 100。这样做可以在随后连续添加大量元素时不发生频繁的内存重分配事件,进而加快执行速度。 ##### 示例二:对比有无 `reserve` 对性能的影响 为了直观感受 `reserve()` 给带来的好处,可以通过计时工具测量两种情况下分别完成相同任务所需的时间差异: ```cpp #include &lt;chrono&gt; #include &lt;iomanip&gt; #include &lt;iostream&gt; #include &lt;vector&gt; using namespace std; double measure_time(bool use_reserve) { constexpr unsigned long N = 1e6UL; vector&lt;double&gt; v; if(use_reserve) v.reserve(N); auto start = chrono::high_resolution_clock::now(); for(unsigned long i = 0; i != N; ++i) v.emplace_back(sqrt(static_cast&lt;double&gt;(i))); auto end = chrono::high_resolution_clock::now(); return chrono::duration&lt;double, milli&gt;(end-start).count(); } int main(){ cout &lt;&lt; fixed &lt;&lt; setprecision(3); double t_with_resrvation = measure_time(true), t_without_reservation = measure_time(false); cout &lt;&lt; &quot;Time taken without reservation: &quot; &lt;&lt; t_without_reservation &lt;&lt; &quot; ms\n&quot;; cout &lt;&lt; &quot;Time taken with reservation: &quot; &lt;&lt; t_with_resrvation &lt;&lt; &quot; ms\n&quot;; cout &lt;&lt; &quot;Speedup factor: &quot; &lt;&lt; t_without_reservation / t_with_resrvation &lt;&lt; endl; } ``` 上述代码展示了带有不带 `reserve()` 调用的情况下填充一百万个浮点数到向量所花费的不同时间长度。通常来说,前者会明显优于后者因为减少了不必要的内存管理开销。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值