記憶體管理與效率

2021-07-05 22:07:04 字數 2723 閱讀 9793

1.使用reserve()函式提前設定容量大小,避免多次容量擴充操作導致效率低下。

關於stl容器,最令人稱讚的特性之一就是是只要不超過它們的最大大小,它們就可以自動增長到足以容納你放進去的資料。(要知道這個最大值,只要呼叫名叫max_size的成員函式。)對於vector和string,如果需要更多空間,就以類似realloc的思想來增長大小。vector容器支援隨機訪問,因此為了提高效率,它內部使用動態陣列的方式實現的。在通過 reserve() 來申請特定大小的時候總是按指數邊界來增大其內部緩衝區。當進行insert或push_back等增加元素的操作時,如果此時動態陣列的記憶體不夠用,就要動態的重新分配當前大小的1.5~2倍的新記憶體區,再把原陣列的內容複製過去。所以,在一般情況下,其訪問速度同一般陣列,只有在重新分配發生時,其效能才會下降。正如上面的**告訴你的那樣。而進行pop_back操作時,capacity並不會因為vector容器裡的元素減少而有所下降,還會維持操作之前的大小。對於vector容器來說,如果有大量的資料需要進行push_back,應當使用reserve()函式提前設定其容量大小,否則會出現許多次容量擴充操作,導致效率低下。

reserve成員函式允許你最小化必須進行的重新分配的次數,因而可以避免真分配的開銷和迭代器/指標/引用失效。但在我解釋reserve為什麼可以那麼做之前,讓我簡要介紹有時候令人困惑的四個相關成員函式。在標準容器中,只有vector和string提供了所有這些函式。

(1) size()告訴你容器中有多少元素。它沒有告訴你容器為它容納的元素分配了多少記憶體。

(2) capacity()告訴你容器在它已經分配的記憶體中可以容納多少元素。那是容器在那塊記憶體中總共可以容納多少元素,而不是還可以容納多少元素。如果你想知道乙個vector或string中有多少沒有被占用的記憶體,你必須從capacity()中減去size()。如果size和capacity返回同樣的值,容器中就沒有剩餘空間了,而下一次插入(通過insert或push_back等)會引發上面的重新分配步驟。

(3) resize(container::size_type n)強制把容器改為容納n個元素。呼叫resize之後,size將會返回n。如果n小於當前大小,容器尾部的元素會被銷毀。如果n大於當前大小,新預設構造的元素會新增到容器尾部。如果n大於當前容量,在元素加入之前會發生重新分配。

(4) reserve(container::size_type n)強制容器把它的容量改為至少n,提供的n不小於當前大小。這一般強迫進行一次重新分配,因為容量需要增加。(如果n小於當前容量,vector忽略它,這個呼叫什麼都不做,string可能把它的容量減少為size()和n中大的數,但string的大小沒有改變。在我的經驗中,使用reserve來從乙個string中修整多餘容量一般不如使用「交換技巧」,那是條款17的主題。)

這個簡介表示了只要有元素需要插入而且容器的容量不足時就會發生重新分配(包括它們維護的原始記憶體分配和**,物件的拷貝和析構和迭代器、指標和引用的失效)。所以,避免重新分配的關鍵是使用reserve盡快把容器的容量設定為足夠大,最好在容器被構造之後立刻進行。

例如,假定你想建立乙個容納1-1000值的vector。沒有使用reserve,你可以像這樣來做:

vectorv;

for (int i = 1; i <= 1000; ++i) v.push_back(i);

在大多數stl實現中,這段**在迴圈過程中將會導致2到10次重新分配。(10這個數沒什麼奇怪的。記住vector在重新分配發生時一般把容量翻倍,而1000約等於210。)

把**改為使用reserve,我們得到這個:

vectorv;

v.reserve(1000);

for (int i = 1; i <= 1000; ++i) v.push_back(i);

這在迴圈中不會發生重新分配。

在大小和容量之間的關係讓我們可以預言什麼時候插入將引起vector或string執行重新分配,而且,可以預言什麼時候插入會使指向容器中的迭代器、指標和引用失效。例如,給出這段**,

string s;

...if (s.size() < s.capacity())

push_back的呼叫不會使指向這個string中的迭代器、指標或引用失效,因為string的容量保證大於它的大小。如果不是執行push_back,**在string的任意位置進行乙個insert,我們仍然可以保證在插入期間沒有發生重新分配,但是,與伴隨string插入時迭代器失效的一般規則一致,所有從插入位置到string結尾的迭代器/指標/引用將失效。

回到本條款的主旨,通常有兩情況使用reserve來避免不必要的重新分配。第乙個可用的情況是當你確切或者大約知道有多少元素將最後出現在容器中。那樣的話,就像上面的vector**,你只是提前reserve適當數量的空間。第二種情況是保留你可能需要的最大的空間,然後,一旦你新增完全部資料,修整掉任何多餘的容量。

2.使用「交換技巧」來修整vector過剩空間/記憶體

有一種方法來把它從曾經最大的容量減少到它現在需要的容量。這樣減少容量的方法常常被稱為「收縮到合適(shrink to fit)」。該方法只需一條語句:vector(ivec).swap(ivec);

表示式vector(ivec)建立乙個臨時vector,它是ivec的乙份拷貝:vector的拷貝建構函式做了這個工作。但是,vector的拷貝建構函式只分配拷貝的元素需要的記憶體,所以這個臨時vector沒有多餘的容量。然後我們讓臨時vector和ivec交換資料,這時我們完成了,ivec只有臨時變數的修整過的容量,而這個臨時變數則持有了曾經在ivec中的沒用到的過剩容量。在這裡(這個語句結尾),臨時vector被銷毀,因此釋放了以前ivec使用的記憶體,收縮到合適。

C C 之vector的記憶體管理和效率

1 vector容器支援隨機訪問,其內部是通過動態陣列實現的 2 當vector執行insert或者push back時,如果此時動態陣列的記憶體不夠用,則會重新分配當前大小的1.5 2倍的新記憶體區,把原來陣列的內容複製過去 3 為了減少動態陣列的不斷分配,通過採用reverse 來提前設定容器的...

window記憶體管理與記憶體原理

windows為每個程序分配了4gb的虛擬位址空間,讓每個程序都認為自己擁有4gb的記憶體空間,4gb怎麼來的?32位 cpu可以取位址的空間為2的32次方,就是4gb 正如16位cpu有20根定址線所有擁有2的20次方的定址空間一樣 當我們在windows中雙擊乙個應用程式圖示後,系統為該應用程式...

C C 記憶體管理與記憶體對齊

判斷大小端 uint32 t word 0xaabbccdd char p char word for int i 0 i 4 i 大端結果 aa bb cc dd 小端結果 dd cc bb aa bss段 bss segment 通常是指用來存放程式中未初始化,或初始化為0的全域性變數,靜態區域...