vector 動態刪除元素,釋放內存的研究

對於查到的相關資料做了梳理
如果一定需要實現內存動態釋放,第四條一定程度上可以滿足需求,但需要注意以下問題:

vector<std::string>(Vec).swap(Vec);
思考? 是否可以通過需要段元素首尾的迭代器來拷貝需要段的元素,再調用swap呢,這樣可能可以一定程度上減少調用erase帶來的大開銷,當然具體哪個比較划算還是根據實際情況來判斷

vector::erase() 函數在容器內元素比較多,刪除元素的代價會比較大,他會把後邊的元素移動至刪除位置,改變vector的size但是並不改變capacity,換句話說並不會釋放這些刪除元素的內存,官方說明如下:

      /**
       *  @brief  Remove a range of elements.
       *  @param  __first  Iterator pointing to the first element to be erased.
       *  @param  __last  Iterator pointing to one past the last element to be
       *                  erased.
       *  @return  An iterator pointing to the element pointed to by @a __last
       *           prior to erasing (or end()).
       *
       *  This function will erase the elements in the range
       *  [__first,__last) and shorten the %vector accordingly.
       *
       *  Note This operation could be expensive and if it is
       *  frequently used the user should consider using std::list.
       *  The user is also cautioned that this function only erases
       *  the elements, and that if the elements themselves are
       *  pointers, the pointed-to memory is not touched in any way.
       *  Managing the pointer is the user's responsibility.
       */
      iterator
      erase(iterator __first, iterator __last);

一、c++ vector clear() 方法的問題:

原文地址:c++ vector clear() 方法的問題

vector,clear()並不真正釋放內存(這是爲優化效率所做的事),clear實際所做的是爲vector中所保存的所有對象調用析構函數(如果有的話),然後初始化size這些東西,讓覺得把所有的對象清除了。
  真正釋放內存是在vector的析構函數裏進行的,所以一旦超出vector的作用域(如函數返回),首先它所保存的所有對象會被析構,然後會調用allocator中的deallocate函數回收對象本身的內存。
  所以,某些編譯器clear後還能訪問到對象數據(因爲它根本沒清除),在一些比較新的C++編譯器上(例如VS2008),當進行數組引用時(例如a[2]這種用法),STL庫中會有一些check函數根據當前容器的size值來判斷下標引用是否超出範圍,如果超出,則會執行這樣一句:
  _THROW(out_of_range, "invalid vector<T> subscript");
  即拋出一個越界異常,clear後沒有捕獲異常,程序在新編譯器編譯後就會崩潰掉。

-------------------------分割線--------------------------------------------------------------

二、vector的內存釋放:

原文地址:vector的內存釋放

vector : C++ STL中的順序容器,封裝數組

1. vector容器的內存自增長

與其他容器不同,其內存空間只會增長,不會減小。先來看看"C++ Primer"中怎麼說:爲了支持快速的隨機訪問,vector容器的元素以連續方式存放,每一個元素都緊挨着前一個元素存儲。設想一下,當vector添加一個元素時,爲了滿足連續存放這個特性,都需要重新分配空間、拷貝元素、撤銷舊空間,這樣性能難以接受。因此STL實現者在對vector進行內存分配時,其實際分配的容量要比當前所需的空間多一些。就是說,vector容器預留了一些額外的存儲區,用於存放新添加的元素,這樣就不必爲每個新元素重新分配整個容器的內存空間。

關於vector的內存空間,有兩個函數需要注意:size()成員指當前擁有的元素個數;capacity()成員指當前(容器必須分配新存儲空間之前)可以存儲的元素個數。reserve()成員可以用來控制容器的預留空間。vector另外一個特性在於它的內存空間會自增長,每當vector容器不得不分配新的存儲空間時,會以加倍當前容量的分配策略實現重新分配。例如,當前capacity爲50,當添加第51個元素時,預留空間不夠用了,vector容器會重新分配大小爲100的內存空間,作爲新連續存儲的位置。

2. vector內存釋放

由於vector的內存佔用空間只增不減,比如你首先分配了10,000個字節,然後erase掉後面9,999個,留下一個有效元素,但是內存佔用仍爲10,000個。所有內存空間是在vector析構時候才能被系統回收。empty()用來檢測容器是否爲空的,clear()可以清空所有元素。但是即使clear(),vector所佔用的內存空間依然如故,無法保證內存的回收。

如果需要空間動態縮小,可以考慮使用deque。如果非vector不可,可以用swap()來幫助你釋放內存。具體方法如下:

vector<int> nums; 
nums.push_back(1);
nums.push_back(1);
nums.push_back(2);
nums.push_back(2); 
vector<int>().swap(nums); //或者nums.swap(vector<int> ())

或者如下所示,使用一對大括號,意思一樣的:

//加一對大括號是可以讓tmp退出{}的時候自動析構
{ 
    std::vector<int> tmp =   nums;  
    nums.swap(tmp); 
}

swap()是交換函數,使vector離開其自身的作用域,從而強制釋放vector所佔的內存空間,總而言之,釋放vector內存最簡單的方法是vector.swap(nums)。當時如果nums是一個類的成員,不能把vector.swap(nums)寫進類的析構函數中,否則會導致double free or corruption (fasttop)的錯誤,原因可能是重複釋放內存。標準解決方法如下:

template < class T >
void ClearVector( vector< T >& vt ) 
{
    vector< T > vtTemp; 
    veTemp.swap( vt );
}

3. 利用vector釋放指針

如果vector中存放的是指針,那麼當vector銷燬時,這些指針指向的對象不會被銷燬,那麼內存就不會被釋放。如下面這種情況,vector中的元素時由new操作動態申請出來的對象指針:

#include <vector> 
using namespace std; 
vector<void *> v;

每次new之後調用v.push_back()該指針,在程序退出或者根據需要,用以下代碼進行內存的釋放:

for (vector<void *>::iterator it = v.begin(); it != v.end(); it ++) 
    if (NULL != *it) 
    {
        delete *it; 
        *it = NULL;
    }
v.clear();

三、vector::clear(),容器vector的clear函數詳解。:

原文地址:vector::clear(),容器vector的clear函數詳解。

最近經常用到vector容器,發現它的clear()函數有點意思,經過驗證之後進行一下總結。

clear()函數的調用方式是,vector temp(50);//定義了50個datatype大小的空間。temp.clear();

作用:將會清空temp中的所有元素,包括temp開闢的空間(size),但是capacity會保留,即不可以以temp[1]這種形式賦初值,只能通過temp.push_back(value)的形式賦初值。

同樣對於vector<vector > temp1(50)這種類型的變量,使用temp1.clear()之後將會不能用temp1[1].push_back(value)進行賦初值,只能使用temp1.push_back(temp);的形式。

下面的代碼是可以運行的。

    #include <iostream>
    #include<vector>
    using namespace std;
    int main(){
    	vector<vector<int>> test(50);
    	vector<int> temp;
	   	test[10].push_back(1);
    	cout<<test[10][0]<<endl;
    	test.clear();
    	for(int i=0;i<51;i++)
    		test.push_back(temp);
    	system("pause");
    	return 0;
    }

但是這樣是會越界錯誤的。

    #include <iostream>
    #include<vector>
    using namespace std;
    int main(){
	    vector<vector<int>> test(50);
	    vector<int> temp;
	    test[10].push_back(1);
	    cout<<test[10][0]<<endl;
	    test.clear();
	    for(int i=0;i<50;i++)
		   	test[i].push_back(1);
	    system("pause");
	    return 0;
    }

並且即使我們使用

    for(int i=0;i<100;i++)
    	test[i].push_back(1);

都是可以的,因爲size已經被清除了。

四、vector容器刪除某些元素且釋放內存:

原文地址:vector容器刪除某些元素且釋放內存

1.size和capacity

size:
指目前容器中實際有多少元素,對應的resize(size_type)會在容器尾添加或刪除一些元素,來調整容器中實際的內容,使容器達到指定的大小。
capacity:
指最少要多少元素纔會使其容量重新分配,對應reserve(size_type new_size)會這置這個capacity值,使它不小於所指定的new_size。
所以用reserve(size_type)只是擴大capacity值,這些內存空間可能還是“野”的,如果此時使用“[ ]”來訪問,則可能會越界。而resize(size_type new_size)會真正使容器具有new_size個對象。
元素訪問方式差別
在對vector進行訪問時,如果使用“[ ]”,則會像變通數組那樣,不進行越界的判斷。
如果使用“at(size_type)”函數則會先進行越界的判斷。

2.應用

經常會用到vector中元素不再使用,需要刪除元素且釋放內存的操作,記錄如下:
iter = strVec.erase(iter);
strVec是容器名稱,iter是迭代器,刪除元素後,迭代器指向下一個元素;
vector<std::string>(Vec).swap(Vec);
將Vec的內存空洞清除;
vector().swap(Vec);
清空Vec的內存;
示例代碼:

#include"iostream"
#include"vector"
#include"string"
using namespace std;
 
int main() {
    vector<string> strVec;
    
    strVec.push_back("Sophia");
    strVec.push_back("and");
    strVec.push_back("Nemo");
    strVec.push_back("are");
    strVec.push_back("good");
    strVec.push_back("people.");
    
    cout << "The capacity of the original vector is: " << strVec.capacity() << endl;
    
    for (vector<string>::iterator iter = strVec.begin(); iter != strVec.end();) {
        if ((*iter) == "and")
            iter=strVec.erase(iter);
        else if((*iter) == "Nemo")
            iter = strVec.erase(iter);
        else
            iter++;
    }
    
    vector<string>(strVec).swap(strVec);
    
    cout << "After deleting two elements in the vector, now the capacity is: " << strVec.capacity() << endl;
    
    vector<string>().swap(strVec);
    
    cout << "After clearing, now the capacity is: " << strVec.capacity() << endl;
    
    system("Pause");
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章