c++ vector 的坑

一個空的vector執行pop_back操做會發生什麼

因爲以前看STL源碼剖析的時候,發現所執行的操做以下:html

 

只是簡單的將末尾的finish迭代器減1後destroy。這讓人產生一個疑問:假如這個vector爲空了,finish=start了,finish再減1不就不在vector的內存控制範圍了麼。因而,我打算看一下vs2013編譯器和g++編譯器的源碼。ios

vs2013的編譯器源碼以下:程序員

#if _ITERATOR_DEBUG_LEVEL == 2
void pop_back()
{ // erase element at end
if (empty())
_DEBUG_ERROR("vector empty before pop");
else
{ // erase last element
_Orphan_range(this->_Mylast - 1, this->_Mylast);
this->_Getal().destroy(this->_Mylast - 1);
--this->_Mylast;
}
}

#else /* _ITERATOR_DEBUG_LEVEL == 2 */
void pop_back()
{ // erase element at end
this->_Getal().destroy(this->_Mylast - 1);
--this->_Mylast;
}
#endif /* _ITERATOR_DEBUG_LEVEL == 2 */</span>c#

該源碼的意思就是,在debug模式下運行,是會檢測vector是否empty的,但在release模式下不會檢測。通過測試,debug下pop_back一個空的vector會報錯,但release沒有,可是release下面,pop_back後這個vector基本上就廢了,你不能再push_back了,會報錯。由於pop_back顯然已經將vector的對象的結構破壞。數組

 

在g++編譯器下測試,其源碼以下:函數

void pop_back() _GLIBCXX_NOEXCEPT
{
--this->_M_impl._M_finish;
_Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish);
}</span>性能

結果也是同樣的,pop_back一個空的vector會破壞整個vector對象,具體的做用就是finish迭代器失效。但有趣的是,下面的代碼能夠運行:測試

vec.push_back(0);
vec.pop_back();
vec.pop_back();
vec[0] = 1;
cout << vec[0];</span>優化

緣由就是,push_back插入對象是 分爲申請空間和構造對象兩步,pop_back的destroy只有析構對象的做用,沒有deallocate的回收空間的做用。因此,pop_back以後,空間是沒有釋放的,vec[0]可以執行賦值行爲並輸出,但這個時候由於finish迭代器的損毀,vec已經不能執行push_back操做了。另外提一下,destroy函數在析構對象的時候,作了進一步的判斷,若是該對象存在有效的析構函數,則調用改析構函數析構之,不然什麼都不作,好比int類型的對象。this


總的來講,pop_back的準確調用須要程序員來保證,在執行pop_back時候最好能預先作一些判斷。

vector clear() 方法 內存釋放問題

本身查到的三處說法的對比:
1、轉自知道的答案:https://zhidao.baidu.com/question/323662520.html?qq-pf-to=pcqq.c2c#
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後沒有捕獲異常,程序在新編譯器編譯後就會崩潰掉。
-------------------------分割線--------------------------------------------------------------
2、轉自博客:https://www.cnblogs.com/summerRQ/articles/2407974.html


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<int>.swap(nums)。當時若是nums是一個類的成員,不能把vector<int>.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();


3、轉自博客:https://blog.csdn.net/hk_john/article/details/72463318


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

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

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

一樣對於vector<vector<datatype> > 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已經被清除了。

轉自:https://blog.csdn.net/tangle001/article/details/47026989

      https://blog.csdn.net/acoolgiser/article/details/81018296

相關文章
相關標籤/搜索