STL vector动态扩容

1. 基本操作

#include<iostream>
#include<vector>
using namespace std;

int main()
{
    vector<int>v1;
    vector<int>v2(4);
    vector<int>v3(1,4);

    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    v1.push_back(5);

    // 这里清除区间,左闭右开
    v1.erase(v1.begin()+2, v1.end()-1);

    v2 = v1;
    if(!v2.empty())
    {
        for(const auto i : v2)
        {
            cout << i << " ";
        }
        cout << endl;
        cout << "v1 size: " << v2.size() << endl;
        v2.~vector();
    }
    return 0;
}


1. push_back和pop_back操作
都只是对尾部进行操作,
push_back:从尾部插入数据时,当数组还有备用空间时就直接插入尾部就行,如果没有就重新寻找更大的空间并将数据赋值过去
void push_back(const T& x)
{
      // 如果还没有到填满整个数组, 就在数据尾部插入
      if (finish != end_of_storage)
      {
            construct(finish, x);
            ++finish;
      }
    // 数组被填充满, 调用insert_aux必须重新寻找新的更大的连续空间, 再进行插入
      else
          insert_aux(end(), x);
}

pop_back: 从尾部删除,使用空间的尾自减并调用析构函数,但是并没有释放内存

void pop_back()
{
     --finish;
     destroy(finish);
}

finish始终都指向最后一个元素的后一个位置的地址.


2. 容器大小的调整
reverse: 修改容器的大小  空间大小
void reverse(size_type n)
{
    // 修改容器的大小要大于之前的容器大小
    
}

resize: size()  使用大小, 变小释放,变大用尾部填充

new_size大于cap,小于2*cap,则用cap = 2*new_size
        大于2*cap,则cap = new_size

void resize(size_type new_size, const T& x)
{
    // 元素大小大于了要修改的大小, 则释放掉超过的元素
      if (new_size < size())
        erase(begin() + new_size, end());
    // 元素不够, 就从end开始到要求的大小为止都初始化x
      else
        insert(end(), new_size - size(), x);
}

2. reverse 和 resize

// vector::reserve
#include <iostream>
#include <vector>

int main ()
{
  std::vector<int>::size_type sz;

  std::vector<int> foo;
  sz = foo.capacity();
  std::cout << "making foo grow:\n";
  for (int i=0; i<1000; ++i) {
    foo.push_back(i);
    //std::cout << "i: " << i << '\n';
    if (sz!=foo.capacity()) {
      sz = foo.capacity();
      std::cout << "capacity changed: " << sz << '\n';
    }
  }

  std::cout << "foo size: " << foo.size() << '\n';
  std::cout << "cap: " << foo.capacity() <<'\n';

  sz = foo.capacity();
  for(int i = 0;i < 1000;i++)
  {
      //std::cout << foo.pop_back() << '\n';
      std::cout << "i: " << i << " cap: " << foo.capacity() << " size: " << foo.size()<< '\n';
      foo.pop_back();
      if (sz!=foo.capacity()) {
        sz = foo.capacity();
        std::cout << "capacity changed: " << sz << '\n';
    }
  }

  return 0;
}

3. 测试

#include <iostream>
#include <vector>
using namespace std;

int main ()
{
    std::vector<int>::size_type sz;
    std::vector<int> foo;
    vector<int>v1;
    for(int i = 0;i < 12;i++)
    {
        v1.push_back(i);
        cout << v1.capacity() << " " << v1.size() << endl;
    }
    cout << "max_size: " << v1.max_size() << endl;
}

可见,VS中按1.5倍扩容,GCC以2倍扩容。

一种不调用析构函数将vector清空的方法:

vector<int>().swap(v1);

4. 扩容因子

实际上,C++标准并没有push_back要用哪个增长因子,这是由标准库的实现者决定的。

如何选取扩容因子呢?

从空间角度:扩容因子越大,预留的空间就越大,浪费的空间也越多

从时间角度:扩展到相同长度下,K越小,扩容的次数越多,时间开销越大

假设扩容因子为k,扩容后的最终长度为n,这意味着需要扩容 $log_kn$ 次,

这元素复制和开辟内存的时间正比于: $t = 1 + k + k^2 + ... + k^{log_kn -1} = \frac{n-1}{k-1}$,可见k越小越好。

如何达到时间和空间的平衡呢?

我们来看一下K = 2时的情况。

每次扩容后capacity的情况如下:1,2,4,8,16,32 ……..

当我们释放了4的空间,我们寻找8的新空间,再次扩容,释放8,寻找16。。

仔细分析,第5次扩容时,需要寻找16的新空间,第4次释放了8,第3次释放了4,第2次释放了2,第1次释放了1,所以 1 + 2 + 4 + 8 = 15 < 16,也就意味着,之前释放的空间,永远无法被下一次的扩容利用,这对内存与cache是非常不友好的。

我们再来看一下K = 1.5的情况。

每次扩容之后capacity的情况为:1,2,3,4,6,9,13,19,28 ……

再按刚才的思路分析一遍,1 + 2 >= 3; 2 + 3 + 4 >= 6; 6 + 9 >= 13 …….

所以,当K为1.5时,显然对内存和cache要友好很多,至少从容量上来说,是存在重复利用的可能性的。

因此,我们可以得出结论,当K = 2时,时间上要比 K = 1.5 占优,而空间上比 1.5 稍有劣势。

理论最优扩容因子是多少呢?

继续刚才的分析,我们希望的是,上几次的空间,存在被下一次扩容时利用的可能性。

也就是 X(n-2) + X(n-1) >= X(n),显然我们也希望时间上也要更好,即X(n-2) + X(n-1) = X(n)

即:1,2,3,5,8,13,21,34,55 。。。。

是不是很熟悉。。。是的,这就是我们的斐波那契数列。。。

那么当N趋于无限大时,取极限,最佳的扩容因子也就是那个最美的数,黄金分割率,1.618。

 

 

参考链接:

1. CSDN_denghe1122-STL vector (一)——扩容原理与内存分配与释放

2. cplusplus-vector

3. CSDN_gettogetto-当面试官问我们vector扩容机制时,他想问什么?

原文链接: https://www.cnblogs.com/lfri/p/12410966.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    STL vector动态扩容

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/371214

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月3日 上午10:39
下一篇 2023年3月3日 上午10:40

相关推荐