1. 程式人生 > >條目十四《使用reserve來避免不必要的重新分配》

條目十四《使用reserve來避免不必要的重新分配》

條目十四《使用reserve來避免不必要的重新分配》

使用vector和string的插入元素的時候,我們是不用擔心記憶體問題的(只要不超過容器的max_size)。因為底層有分配子管理記憶體。在插入元素的時候,記憶體不夠會發生像realloc的過程:

  1. 分配新的記憶體塊,它有容器目前容量的幾倍。在大部分實現中,vector和string的容量每次以2為因數增
    長。也就是說,當容器必須擴充套件時,它們的容量每次翻倍。
  2. 把所有元素從容器的舊記憶體拷貝到它的新記憶體。
  3. 銷燬舊記憶體中的物件。
  4. 回收舊記憶體

先看個例子

vector<int> vec;
for(int i = 0; i < 10; i++)
{
    vec.push_back(i)
}

這個過程會發生最少4次重新分配記憶體的過程。再來看下一個重新分配記憶體涉及到哪些過程:

  • 1.new操作-->malloc
  • 2.拷貝操作
  • 3.析構物件
  • 4.釋放記憶體

咋一看,一兩次還可以接受,當插入的元素越來越多的時候,對效能的消耗是非常客觀的。

上面的問題還只是其中一個問題,在插入元素時,重新分配記憶體會造成迭代器、指標和引用的失效。

為了優化這兩個問題,在使用vector和string的時候,可以使用reserve函式。先引出四個關於容器大小和設定的成員函式:

  • size()———————獲得容器的元素個數

  • capacity()———獲得容器的容量

  • resize()—————強制設定容器的元素個數(引數大於當前大小,呼叫預設建構函式建立元素填充在尾部。小於當前大小,會析構並銷燬多餘的元素)

  • reserve()————強制設定容器的容量大小(引數小於現有的容量大小會忽略當前呼叫,大於會擴容。)

為了避免容器的不必要擴容而造成的消耗,在初始化容器的時候可以通過reserve()設定容器的容量大小,這樣在插入元素的是夠,只要當前的元素個數小於容量大小,都不會發生重新分配記憶體。這樣也就不會發生迭代器、指標和引用失效等問題,也就沒有多次拷貝,析構物件,釋放記憶體的現象發生。

vector<int> vec;
vec.reserve(15);
for(int i = 0; i < 10; i++)
{
    vec.push_back(i)
}

這個過程在reserve後插入元素不會發生重新分配記憶體過程。因為,插入的元素個數(10)小於容量的大小(15)。

可能有人說,在開始時設定過多的容量,那麼不就相當於陣列嗎?浪費了剩餘沒用到記憶體,不用擔心這個問題,我們可以等插入元素完畢的時候:

  • 呼叫sesize()來修剪大小
  • 使用swap()技巧。(vector