1. 程式人生 > >C++Primer筆記-----day07

C++Primer筆記-----day07

==========================================================================
day07
==========================================================================
1.io物件不能拷貝或賦值,進行io操作的函式,通常以引用方式傳遞和返回流。讀寫一個io物件會改變其狀態,所以傳遞和返回的引用不能是const的。
2.【在使用基類物件的地方,可以用子類物件來代替】。
==========================================================================
vector介紹
1.vector用於操作大小的函式有size(),max_size(),capacity().
size()返回當前vector中的元素個數。可通過resize(n)函式改變其值,即將元素數量改為n。
max_size()返回vector能夠容納的最大元素數量,其值因vector實現的版本而異。
capacity()返回vector中實際能容納的元素數量。可通過reserve(n)函式改變其值,即預留n個記憶體。
2.當我們對容器賦值元素時,源容器的所有元素被拷貝到目標容器中,目標容器中原來的所有元素全被移除。所以,容器的賦值操作代價比較昂貴。
如果兩個容器型別相同,而且拷貝後源容器不再被使用,我們可以用一個簡單的優化方法,swap()。 dst.swap(src)。
swap()的效能比賦值高的多,因為它只交換容器內部資料,事實上,它只交換某些內部指標。時間複雜度為常數。
注意:swap交換內容後,兩個容器的容量也會互換。所以可以這樣縮減一個vector的容量:
template<class T>
void shrinkCapacity(vector<T>& v)
{
vector<T> temp(v);
v.swap(temp);
}


3.vector的容量很重要,因為:
(1)一旦記憶體重新配置,和vector元素相關的指標、引用、迭代器都會失效。
(2)記憶體的重新配置很耗時。
所以,當我們的程式管理了vector元素相關的針、引用、迭代器,或者程式的效率要求很高時,就必須考慮容量問題了。
一種方法是,可以使用reserve()保留適當的容量,避免容量不足時重新分配記憶體。這樣,只要保留的容量尚有餘裕,我們就不用擔心引用失效了。

4.注意,迭代器支援的算術操作 iter1 - iter2 iter1+n <=、>=、>、< 只可以用於string、vector、deque、array。 因為他們在記憶體中的空間是連續的,
而像list就不可以使用這些算術操作。
eg: list<int> lst1;
auto iter1 = lst1.begin();
auto iter2 = lst1.end();
while(iter1<iter2) {/*....*/} // 錯誤,list不支援 < 操作

5.array中,大小也是其型別的一部分。如array<int,10>
標準array具有固定大小,初始化array時,元素數目必須小於或等於其大小,如果小於,則剩餘元素預設初始化。
不用於陣列,一個array可以拷貝給另一個array,但兩個array必須是相同型別(包括大小,大小也是其型別的一部分)。
注意:array不支援賦值操作assign。因為右邊的物件可能與左邊的物件大小不相等。

6.swap即提供了成員函式的版本,又提供了非成員版本。 統一使用非成員版本的swap是一種好習慣。

7.insert函式的功能是在容器的某個迭代器位置【前】插入一個或多個元素。為什麼是在迭代器之前呢?這是因為迭代器可能指向的是容器尾部不存在的元素的位置。
四種insert:
c.insert(it,"hello"); //在指定位置插入一個元素
c.insert(it,10,"A"); //在指定位置插入多個元素,並初始化為A
c.insert(it,v.begin(),v.end()); //將一個範圍內的元素插入it之前
c.insert(it,{"abc","def"}); // 使用初始化列表來插入

insert函式的返回值,其返回第一個新加入元素的迭代器。利用這一點,可以在某個特定位置反覆插入。

8.新標準下引入的emplace_front、emplace、emplace_back分別對應於push_front、insert、push_back。
它們的區別在於,呼叫push和insert時,我們將元素型別的物件傳遞給它們,這些物件被拷貝到容器中,
而呼叫emplace時,將引數傳遞給元素型別的建構函式,emplace使用這些引數在容器管理的記憶體空間直接構造元素。

eg: vector<PersonInfo> c; //假設PersonInfo型別有預設建構函式
c,emplace_back(); // 使用PersonInfo的預設建構函式,將一個元素新增到容器中
c.emplace_back("Jack",22,"15110634483");
c.push_back("Jack",22,"15110634483"); //錯誤,沒有接受三個引數版本的push_back
c.push_back(PersonInfo("Jack",22,"15110634483")); //正確,建立一個臨時PersonInfo物件傳遞給push_back

9. 訪問容器中的元素:
每個順序容器都定義了front(),除forward_list外,都定義了back()。
對於string、vector、deque、array,還可以使用下標運算子[]和at()來訪問容器中的元素。
注意:front()、back()、[]、at()返回的都是引用!如果容器是一個const物件,則返回const引用,如果不是,則返回普通引用。
eg: vector<int> a(10,5);
a.front() = 5; // 將5賦給a中的第一個元素
auto v = a.front(); // v不是一個引用,它是a.front()的一個拷貝
v = 20; // a.front的值沒有被改變
auto &v2 = a.front(); // v2是一個引用,他將改變a.front的值
v2 = 30;

10. 刪除容器中的元素: pop_front()、pop_back()、erase()、clear()
vector、string不支援pop_front(),forward_list不支援pop_back(),而且他還有特殊版本的erase
其中pop操作、clear()返回void,而erase()返回被刪除的最後一個元素之後的迭代器。

11. 對於forward_list,它有自己的插入,刪除操作。它是一個單鏈表,這些操作的前提是要知道訪問結點的前驅結點。
於是,它定義了before_begin(),cbefore_begin()返回首前迭代器,即首元素之前的不存在的元素的迭代器。
insert_after() 返回最後一個插入元素的迭代器
emplace_after() 返回指向新元素的迭代器
erase_after() 返回被刪除元素之後元素的迭代器

12.增刪元素都可能使得迭代器失效。這時,為保證後續迭代器不失效,應該這樣做:
eg:vector<int> vi = {0,1,2,3,4,5,6,7,8,9};
auto iter = vi.begin();
while(iter!=vi.end()){
if(*iter%2){
iter = vi.insert(iter,*iter); // 如果為奇數,複製當前元素
iter += 2; // 為保證後續迭代器有效,向前移動迭代器,跳過當前元素和插入到它之前的元素
}else{
iter = vi.erase(iter); // 刪除偶數元素
// 此時不需要移動迭代器,因為erase返回的就是我們刪除的元素之後的元素
}
}
此程式碼還有一處重點需要注意: while(iter!=vi.end()) 迴圈判斷條件必須每次都呼叫vi.end(),
而不能這樣: auto e = vi.end();
while(iter!=e){/*...*/} // 因為每次增刪元素都會使得尾置迭代器失效,必須重新獲取!

13. 順序介面卡 stack、queue、priority_queue
stack和queue預設是基於deque實現的,而priority_queue是基於vector實現的