C++ primer學習筆記--第9章(2)

順序容器

1.順序容器操作

前面介紹的那些是所有容器都支持的,我們接下來介紹的只適用於順序容器(以後還會介紹關聯容器)。

(1)添加元素

不知道爲啥原書篇幅超多,我覺得直接看代碼就很明瞭,所以我就寫代碼了:

list<int> a = {1, 2, 3}; //註釋爲a的元素內容{1, 2, 3}
a.push_back(4); //{1, 2, 3, 4},array和forwar_list不支持
a.push_front(0); //{0, 1, 2, 3, 4},只有list,forward_list和deque支持

以上兩種可以方便地在頭尾添加元素,更進一步,insert允許我們在容器任意位置中插入多個元素,vector,deque,list,string都支持insert:每個insert接受一個迭代器作爲第一個參數:

list<string> slist = {Jay"};
auto iter = slist.begin();
slist.insert(iter, "Hello"); //將Hello添加到iter之前的位置

//insert還有重載版本
vector<string> svec;
svec.insert(svec.end(), 10, "May"); //在末尾插10個May
vector<string> a = {"1", "2"};
slist.insert(slist.begin(), a.end()-1, a.end()); //插入了"2"
slist.insert(slist.begin(), slist.begin(), slist.end());
//錯誤,要拷貝的迭代器不能指向自己

現在我要在vector中實現一個功能,每次插入的單詞都插在頭部:

vector<string> a;
auto it = a.begin();
string word;
while(cin >> word)
{
it = a.insert(it, word);
}

因爲insert會返回當前位置,調用insert會在前一個位置插入,相當於調用了push_front

新標準還引入了三個新成員-emplace_front,emplace和emplace_back,這些操作時構造元素,不是拷貝元素,它們仨分別於push_front,insert和push_back對應:

vector<Sales_data> c;
c.emplace_back("1", 25, 16.8);
//在c的末尾構造一個Sales_data對象(調用三個參數的構造函數)

c.emplace_back("1", 25, 16.8); //這樣不對的
c.emplace_back(Sales_data("1", 25, 16.8)) //這樣可以

(2)訪問元素

每個順序容器都有一個front函數,返回首元素的引用;除forward_list之外的都有一個back,返回尾元素的引用,還是看代碼好理解:

vector<string> c = {"1", "2"};
if(!c.empty)
{
auto v1 = *c.begin(), v2 = c.front(); //v1,v2都是首元素的拷貝

auto last = c.end();
auto v3 = *(--last); //除forward_list之外
auto v4 = c.back(); //也除forward_list之外
}

c[n]和c.at(n) 返回下標爲n的元素的引用:

if(!c.empty)
{
auto &v = c.back(); //獲得尾元素的引用
v = “3”; //改變了c中的元素
auto v2 = c.back();
v2 = "0"; //沒改c的元素,是個引用
}

與以前一樣,如果用auto變量保存這些函數的返回值,並且要改變容器內的值,必須要將變量定義爲引用類型。 下標一定要在合理範圍內,這個程序員自己要注意,來個錯誤示範:

vector<int> a;
cout << a[0]; //錯了

(3)刪除元素

vector<int> a = {1, 2, 3, 4, 5, 6};
a.pop_front(); //刪除首元素
a.pop_back(); //刪除尾元素

//刪除a中所有奇數
auto it = a.begin();
while(it != a.end())
{
if(*it % 2)
{
it = a.eraser(it); //刪除奇數
}
else
{
++it;
}
}

//刪除所有元素,兩種方式
a.clear();
a.eraser(a.begin(), a.end());

(4)內存管理

vector對象是如何增長的

這一部分我們來仔細研究一下vector在內存中的管理。
我們已經學過,vector的元素在內存中是連續存儲的,這樣一來,如果我在添加元素的時候,沒有空間去容納新元素應該怎麼辦呢?vector是這麼做的:

  1. 把已有元素移動到新空間中
  2. 然後添加新元素
  3. 釋放舊存儲空間 那麼問題又來了,我們的新空間要分配多大呢?一般來說貌似是舊空間的兩倍,反正就是會預留一些空間。

管理容量的成員函數

成員函數 作用
c.size() 目前含有元素的數量
c.capacity() 所能保存的最大元素數量(不重新分配內存空間),只適用vector和string
c.shrink_to_fit() 將capacity()減小爲size()相同大小(適用vector、string、deque)
c.reserve(n) 分配至少能容納n個元素的內存空間

reserve():只有噹噹前內存空間不夠用的時候,reserve纔會重新分配內存空間(可能比n還要大),夠用的話這個函數什麼也不做,這樣的話,reserve永遠不會減少容器所佔用的內存空間。

還有一個resize函數已經用過的,它只改變容器中元素的個數,不會改變容器的容量。

capacity和size

直接看代碼:

vector<int> ivec;
ivec.push_back(1);
cout << ivec.size() << ivec.capacity();

size肯定是1,capacity依賴於標準庫的具體實現,肯定大於等於1


(5)改變容器大小

除了array之外,我們可以用resize來改變容器大小:

  • 如果當前大小大於所要求的的大小,容器後面的元素會被刪除
  • 如果當前大小小於新大小,會將新元素添加到容器後部
    list<int> a(10, 42); //10個42
    a.resize(15); //後面再加5個0,是默認初始化的,
    //如果是類類型,要麼就有默認構造函數,要麼就提供初始值
    a.resize(25, -1); //後面再加10個-1
    a.resize(5); //從末尾刪除20個元素,就剩5個42
    縮小容器,指向被刪除元素的迭代器、引用和指針都會失效;對vector、string和deque進行resize也可能導致它們失效。

容器操作可能使迭代器失效

使用失效的指針、引用或迭代器是很嚴重的錯誤,我們來仔細分析一下,分添加和刪除兩種情況
添加元素:

  • 容器是vector或string:
  • 如果存儲空間被重新分配,則指針等全部失效
  • 未重新分配,插入位置之後的那些都失效
    總之就是內存位置變了就不行了
  • 對於deque,插入除了首尾之外的位置就會失效;如果在首尾添加,迭代器失效,引用和指針不會失效
  • 對於list和forward_list,都還是有效的

刪除元素:
(被刪除的元素對應的那三樣肯定掛了)

  • 對於list和forward_list都有效
  • 對於deque,
  • 如果在首尾之外的任何位置刪除,都失效;
  • 如果刪除尾元素,尾後迭代器失效,其他不影響,如果刪除首元素
  • 如果刪除首元素,都不受影響
  • 對於vector和string,被刪除元素之前的都有效,所以尾後迭代器總會失效
注意函數返回迭代器的位置

我們來寫一個函數,刪除偶數元素,複製每個奇數元素:

vector<int> vi = {0, 1, 2, 3, 4, 5};
auto iter = vi.begin();
while(iter != vi.end())
{
if(*iter % 2)
{
iter = vi.insert(iter, *iter);
iter += 2; //跳過當前元素和複製元素
}
else
{
iter = vi.eraser(iter); //刪除偶數元素
//不用向前移動迭代器,因爲eraser返回刪除元素的下一個
}
}
不要保存end返回的迭代器
vector<int> vi = {0, 1, 2, 3, 4, 5};
auto begin = vi.begin();
auto end = vi.end();
while(begin != end)
{
begin = v.insert(begin, 42);
++begin; //跳過剛剛加入的元素
}

有問題的,因爲end記住了一個以前的end,更好的應該這樣:

vector<int> vi = {0, 1, 2, 3, 4, 5};
auto begin = vi.begin();
while(begin != vi.end())
{
begin = v.insert(begin, 42);
++begin; 
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章