版權聲明:原創文章,歡迎轉載,但請註明出處,謝謝。
https://blog.csdn.net/qiuguolu1108/article/details/107146184
文章目錄
vector
是一個封裝了動態大小數組的順序容器,它能夠存放各種類型的對象。 可以刪除元素、可以插入元素、可以查找元素,做這些工作我們無需管理容器內存。容器內存管理,這種髒活累活全部交由vector管理。瞭解一下vector的內存管理策略,能夠更加充分的利用內存。
1、定義一個用於測試的類
class A
{
public:
A(int data = 100)
:data_(data)
{
construct_count_++;
cout<<"constructor : "<<this<<endl;
}
A(const A& a)
{
copy_construct_count_++;
cout<<this<<" : copy constructor form : "<<&a<<endl;
}
static void dis_construct_count()
{
cout<<"construct count: "<<construct_count_<<endl;
}
static void dis_copy_construct_count()
{
cout<<"copy construct count: "<<copy_construct_count_<<endl;
}
void display()
{
cout<<data_<<endl;
}
~A()
{
cout<<"deconstructor : "<<this<<endl;
}
private:
int data_;
static int construct_count_;
static int copy_construct_count_;
};
int A::construct_count_ = 0;
int A::copy_construct_count_ = 0;
定義一個類,加上一些測試打印信息,幫助我們測試vector。
2、vector內存分配策略
vector是一個動態數組,它會根據元素的個數,適當的去申請內存。可以簡單的把vector理解爲,其內部有一個void*指針,用於指向在堆上申請的空間。void*指向的空間用於存放vector元素。vector一直維護着void*空間的大小,當void*指向的堆空間沒有空間存放新插入的元素時,它都會去系統申請之前空間大小的兩倍空間,並把之前的元素全部拷貝到新的空間,釋放掉原空間,在新空間中插入新的元素。
3、vector使用示例
通過一個vector示例,說明vector是如何管理內存的。
vector<A> va;
A a,b,c,d,e;
cout<<endl<<"======================"<<endl;
va.push_back(a);
cout<<endl<<"======================"<<endl;
va.push_back(b);
cout<<endl<<"======================"<<endl;
va.push_back(c);
va.push_back(d);
cout<<endl<<"======================"<<endl;
va.push_back(e);
cout<<endl;
A::dis_construct_count();
A::dis_copy_construct_count();
cout<<endl;
運行結果如下圖:
結合運行的結果,分析一下這些結果是怎麼打印出來的。
3.1 生成vector<A> va容器對象
vector<A> va;
這條語句僅生成了va對象,並沒有爲va對象分配堆上空間。可以假設其內部的數據成員A*指向了nullptr。
3.2 構造5個A類型的對象
A a,b,c,d,e;
這個運行結果很好理解,就是通過構造器生成了5個類A對象。
3.3 向vector中推入對象a
cout<<endl<<"======================"<<endl;
va.push_back(a);
在向vector中推入元素之前,vector還沒有分配保存類A對象的空間,再調用push_back()的時候,發現vector空間爲空,則先去堆上申請一個空間,用於存放對象a。再將對象a存入vector的時候,發生了一次拷貝構造。
3.4 向vector中推入對象b
cout<<endl<<"======================"<<endl;
va.push_back(b);
再次向vector推入對象b,push_back()函數發現va容器中沒有空間了,則它會再向系統申請兩倍之前的空間。將之前的元素a拷貝到新空間,並將新元素b插入到a元素之後。最後將原來的空間釋放掉,也就是原來的a對象會被釋放掉。
3.5 向vector中推入對象c、d
cout<<endl<<"======================"<<endl;
va.push_back(c);
va.push_back(d);
向va容器推入c對象,push_back()發現va沒有空間了,現在容器中有兩個元素,按照內存分配策略,這次push_back()內部的函數會向系統申請4個元素的空間,並把之前容器中的元素a、b全部拷貝到新空間,並釋放原空間。之後插入元素c。再次插入d對象時,此時va容器正好有一個元素空間空閒,所以剛好把d對象放下。
3.6 向vector中推入對象e
cout<<endl<<"======================"<<endl;
va.push_back(e);
插入e對象的過程,和上面類似,如下圖:
3.7 對象a、b、c、d、e和va容器的析構
類A對象的析構,直接調用其析構器就可以了。va容器的析構,首先要調用容器元素的析構器,再釋放va所佔用的空間。
3.8 vector會發生元素的大量搬動
vector<A> va;
A a;
for(int i=0;i<1024;i++)
{
va.push_back(a);
}
A::dis_construct_count();
A::dis_copy_construct_count();
理想的情況下,應該只發生1024次拷貝構造,但這裏卻發生了2047次拷貝,多了近一半的"多餘"拷貝構造。這不是我們想要的,大量的搬動元素,會早造成性能降低。
4、size()和capacity()函數
vector的成員函數size(),表示容器中實際存放元素的個數。capacity()函數表示vector此刻總共可以容納元素的個數。其中capacity()函數的返回值纔是vector實際佔用空間的大小。
vector<A> va;
A a,b,c,d,e;
va.push_back(a);
va.push_back(b);
va.push_back(c);
va.push_back(d);
va.push_back(e);
cout<<"size = "<<va.size()<<endl;
cout<<"capacity = "<<va.capacity()<<endl;
根據上面的分析,我們可知,現在的va向系統申請了8個可以存放類A的空間,其中va只使用了前5個空間,最後3個空間並沒有使用。
重點
:capacity()纔是vector佔用的實際空間,size()僅是vector使用空間的個數,極端情況下capacity()是size()的兩倍左右。
vector<int> vi;
for(int i=0;i<1024+1;i++)
{
vi.push_back(i);
}
cout<<"size = "<<vi.size()<<endl;
cout<<"capacity = "<<vi.capacity()<<endl;
雖然存放了1025個元素,但vi容器卻佔用着2048個元素,空間有些浪費。
5、總結
本文通過一個示例,介紹了vector內存分配的策略。