C++自修入門實境秀、C++ Primer 5版研讀秀 56/ ~ v9 Sequential Containers 9.4. How a v...







9.3.2. Accessing Elements

2:00若容器裡頭沒有元素,則存取運算將是未定義的(undefined)所以在使用存取運算前一定要確定容器內有元素才行

所有的循序容器(sequential container)——包括arrary都有front成員函式(方法),而只有forward_list沒有back成員函式

front、back成員函式傳回的分別是對第一個和最後一個元素的參考



頁347

6:40

at和下標運算子([]運算子,subscript operator)不能套用在list上,list是不支援隨機存取元素的,僅支援循序存取(見頁326,Table 9.1. Sequential Container Types)

表9.6 :存取一個循序容器中元素的運算

at 和下標運算子(subscript operator)只對 string、vector、deque 和 array 有效。 back 對 forward_list 無效。

c.back() 回傳一個參考指向c中最後一個元素。如果c是空的,就是未定義。

c.front() 回傳一個參考指向c中第一個元素。如果c是空的,就是未定義。

c[n] 回傳一個參考指向由無號整數值n所索引的元素。如果n >= c.size (), 就是未定義。

c.at(n) 回傳一個參考指向n所索引的元素。如果索引超出範圍,就擲出out_of_ range例外。

WARNING

在一個空的容器上呼叫front或back,就像用了一個範圍外的下標,是嚴重的程 式設計錯誤。

The Access Members Return References

存取成員函式回傳的是參考型別

傳回的參考是否是一個常值的參考,取決於container是否是常值const

auto v2 = c.back(); // v2 is not a reference; it's a copy of c.back()

v2 = 0; // no change to the element in c

如果想改變元素值,就必須用參考型別來儲存back與front成員函式回傳的值

41:30

Subscripting and Safe Random Access

頁348

at成員函式原來是這個用途:

If we want to ensure that our index is valid, we can use the at member instead. The at member acts like the subscript operator, but if the index is invalid, at throws an out_of_range exception (§ 5.6 , p. 193 ):

幫助檢查下標(subscript)運算用的索引值是否有效

47:50

練習9.23

都是c元素的複本

練習9.24

#include<vector>

#include<iostream>

using namespace std;

int main() {

vector<int>iv;// {2, 4, 6, 8, 10, 12, 14, 25, 33, 1};

try

{

auto v1=iv.at(0);

}

catch (const std::exception&)

{

cout << "error!"<< endl;

}

//auto v2=*(iv.begin());

//auto v3 = iv[0];

//auto v4 = iv.front();

}

9.3.3. Erasing Elements

1:2:49



1:18:55

The pop_front and pop_back Members

Just as there is no push_front for vector and string , there is also no pop_front for those types.



頁349

1:4:49

Table 9.7. erase Operations on Sequential Containers 表9.7 :循序容器上的erase運算

這些運算會改變容器的大小,所以array沒有支援。 forward_List有一個特殊版本的erase,參閱§9.3.4。(頁351)

pop_back 對 forward_list 無效;pop_front 對 vector 和 string 無效。

c.pop_back () 移除c中的最後一個元素。如果c為空,就是未定義。回傳void。

c.pop_front () 移除c中的第一個元素。如果c為空,就是未定義。回傳void。

c.erase(p) 移除迭代器p所代表的元素,並回傳一個迭代器指向被删除元素的後一 個元素,如果p代表最後一個元素,那就回傳off-the-end (尾端後)的 迭代器。如果p是off-the-end迭代器,就是未定義。

c.erase(b,e) 移除迭代器b和e所代表的範圍中的元素。回傳一個迭代器指向最後一 個被删除的元素後的元素,或在e本身是off-the-end迭代器的時候,回 傳一個off-the-end迭代器。

c.clear () 移除c中的所有元素。回傳void。

WARNING

在一個deque的開頭或尾端以外的任何地方移除元素,會使所有的迭代器、參考 和指標無效化。指向一個vector或string中在移除點之後元素的迭代器、參考 和指標也會無效化。

1:24:20

Removing an Element from within the Container

pop是不傳回值,而erase會傳回迭代器型別,此迭代器是指向被刪除的其後一個元素

1:32:30

Removing Multiple Elements

elem1 = slist.erase(elem1, elem2); // after the call elem1 == elem2

因為迭代器範圍是左包含區間(left-inclusive interval),所以elem2所指的不會被刪除

頁350

練習9.25

#include<list>

#include<string>

using namespace std;

int main() {

list<string>slist{ "孫守真","士林區","范仲淹","江蘇省","連橫","臺灣","包拯","安徽省" };



list<string>::const_iterator elem1 = slist.cbegin() , elem2 = slist.cend() ;

++elem1; elem1 = elem2;

elem1 = slist.erase(elem1,elem2);// no element be delete,when elem1==elem2

//if elem2 is off-the-end ,the elements after elem1 and elem1 all be deleted

// when elem1==elem2==off-the-end ,there is no element would be deleted

}

1:59:55

練習9.26

#include<list>

#include<vector>

using namespace std;

int main() {

int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89 };

list<int>lst;

vector<int>vec;

for (int i : ia)

{

lst.push_back(i); vec.push_back(i);

}

list<int>::const_iterator p=lst.cbegin();

while (p != lst.cend()) {

if ((*p) % 2)

p = lst.erase(p);

else

++p;

}

vector<int>::const_iterator v=vec.cbegin();

while (v != vec.cend()){

if ((*v) % 2 == 0)

v = vec.erase(v);

else ++v;

}

}

2:20:15

2:39:20

Word VBA

DAO 改用 ADO recordset

3:51:00

9.3.4. Specialized forward_list Operations

forward_list容器有其獨特的運算

4:41:50

To understand why forward_list has special versions of the operations to add and remove elements, consider what must happen when we remove an element from a singly linked list.

4:46:40

Figure 9.1. forward_list Specialized Operations

4:50:00

移除一個元素,則其前一個元素就會跟著被影響:

When we add or remove an element, the element before the one we added or removed has a different successor.

可見這裡是把elem4當作elem3之前(before)的元素,因為從elem3移至elem4叫advance(前進),所以前端是在右邊。一旦移除了elem3,那麼elem4的接班人(successor,後面跟著的)就會是elem2了

To add or remove an element, we need access to its predecessor in order to update that element’s links.

4:58:00

In a singly linked list there is no easy way to get to an element’s predecessor. For

在單向連結串列是沒有簡便的辦法去取得一個元素的前位元素(前置元素, predecessor)

就是因為這樣,forward_list就另外定義了insert_after,emplace_after,erase_after這樣的成員函式,並且還定義了一個名為before_begin的成員函式來取得「an off-the-beginning iterator」在第一個元素之前的那個位置的迭代器

頁351

這裡的after還是右端的嘛,要移除elem3就在elem2上呼叫erase_after,意指elem3就在elem2之後嘛



觀念題!

可見「sucessor、predecessor」和「after、before」乃至「back、front」是不同的概念,因為「sucessor、predecessor」是針對循序容器(sequential container)單向連結串列(singly linked list)而言的,所以它的advance前端(predecessor)就是這裡的after或back




9.3.2. Accessing Elements

2:00若容器裡頭沒有元素,則存取運算將是未定義的(undefined)所以在使用存取運算前一定要確定容器內有元素才行

所有的循序容器(sequential container)——包括arrary都有front成員函式(方法),而只有forward_list沒有back成員函式

front、back成員函式傳回的分別是對第一個和最後一個元素的參考



頁347

6:40

at和下標運算子([]運算子,subscript operator)不能套用在list上,list是不支援隨機存取元素的,僅支援循序存取(見頁326,Table 9.1. Sequential Container Types)

表9.6 :存取一個循序容器中元素的運算

at 和下標運算子(subscript operator)只對 string、vector、deque 和 array 有效。 back 對 forward_list 無效。

c.back() 回傳一個參考指向c中最後一個元素。如果c是空的,就是未定義。

c.front() 回傳一個參考指向c中第一個元素。如果c是空的,就是未定義。

c[n] 回傳一個參考指向由無號整數值n所索引的元素。如果n >= c.size (), 就是未定義。

c.at(n) 回傳一個參考指向n所索引的元素。如果索引超出範圍,就擲出out_of_ range例外。

WARNING

在一個空的容器上呼叫front或back,就像用了一個範圍外的下標,是嚴重的程 式設計錯誤。

The Access Members Return References

存取成員函式回傳的是參考型別

傳回的參考是否是一個常值的參考,取決於container是否是常值const

auto v2 = c.back(); // v2 is not a reference; it's a copy of c.back()

v2 = 0; // no change to the element in c

如果想改變元素值,就必須用參考型別來儲存back與front成員函式回傳的值

41:30

Subscripting and Safe Random Access

頁348

at成員函式原來是這個用途:

If we want to ensure that our index is valid, we can use the at member instead. The at member acts like the subscript operator, but if the index is invalid, at throws an out_of_range exception (§ 5.6 , p. 193 ):

幫助檢查下標(subscript)運算用的索引值是否有效

47:50

練習9.23

都是c元素的複本

練習9.24

#include<vector>

#include<iostream>

using namespace std;

int main() {

vector<int>iv;// {2, 4, 6, 8, 10, 12, 14, 25, 33, 1};

try

{

auto v1=iv.at(0);

}

catch (const std::exception&)

{

cout << "error!"<< endl;

}

//auto v2=*(iv.begin());

//auto v3 = iv[0];

//auto v4 = iv.front();

}

9.3.3. Erasing Elements

1:2:49



1:18:55

The pop_front and pop_back Members

Just as there is no push_front for vector and string , there is also no pop_front for those types.



頁349

1:4:49

Table 9.7. erase Operations on Sequential Containers 表9.7 :循序容器上的erase運算

這些運算會改變容器的大小,所以array沒有支援。 forward_List有一個特殊版本的erase,參閱§9.3.4。(頁351)

pop_back 對 forward_list 無效;pop_front 對 vector 和 string 無效。

c.pop_back () 移除c中的最後一個元素。如果c為空,就是未定義。回傳void。

c.pop_front () 移除c中的第一個元素。如果c為空,就是未定義。回傳void。

c.erase(p) 移除迭代器p所代表的元素,並回傳一個迭代器指向被删除元素的後一 個元素,如果p代表最後一個元素,那就回傳off-the-end (尾端後)的 迭代器。如果p是off-the-end迭代器,就是未定義。

c.erase(b,e) 移除迭代器b和e所代表的範圍中的元素。回傳一個迭代器指向最後一 個被删除的元素後的元素,或在e本身是off-the-end迭代器的時候,回 傳一個off-the-end迭代器。

c.clear () 移除c中的所有元素。回傳void。

WARNING

在一個deque的開頭或尾端以外的任何地方移除元素,會使所有的迭代器、參考 和指標無效化。指向一個vector或string中在移除點之後元素的迭代器、參考 和指標也會無效化。

1:24:20

Removing an Element from within the Container

pop是不傳回值,而erase會傳回迭代器型別,此迭代器是指向被刪除的其後一個元素

1:32:30

Removing Multiple Elements

elem1 = slist.erase(elem1, elem2); // after the call elem1 == elem2

因為迭代器範圍是左包含區間(left-inclusive interval),所以elem2所指的不會被刪除

頁350

練習9.25

#include<list>

#include<string>

using namespace std;

int main() {

list<string>slist{ "孫守真","士林區","范仲淹","江蘇省","連橫","臺灣","包拯","安徽省" };



list<string>::const_iterator elem1 = slist.cbegin() , elem2 = slist.cend() ;

++elem1; elem1 = elem2;

elem1 = slist.erase(elem1,elem2);// no element be delete,when elem1==elem2

//if elem2 is off-the-end ,the elements after elem1 and elem1 all be deleted

// when elem1==elem2==off-the-end ,there is no element would be deleted

}

1:59:55

練習9.26

#include<list>

#include<vector>

using namespace std;

int main() {

int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89 };

list<int>lst;

vector<int>vec;

for (int i : ia)

{

lst.push_back(i); vec.push_back(i);

}

list<int>::const_iterator p=lst.cbegin();

while (p != lst.cend()) {

if ((*p) % 2)

p = lst.erase(p);

else

++p;

}

vector<int>::const_iterator v=vec.cbegin();

while (v != vec.cend()){

if ((*v) % 2 == 0)

v = vec.erase(v);

else ++v;

}

}

2:20:15

2:39:20

Word VBA

DAO 改用 ADO recordset

3:51:00

9.3.4. Specialized forward_list Operations

forward_list容器有其獨特的運算

4:41:50

To understand why forward_list has special versions of the operations to add and remove elements, consider what must happen when we remove an element from a singly linked list.

4:46:40

Figure 9.1. forward_list Specialized Operations

4:50:00

移除一個元素,則其前一個元素就會跟著被影響:

When we add or remove an element, the element before the one we added or removed has a different successor.

可見這裡是把elem4當作elem3之前(before)的元素,因為從elem3移至elem4叫advance(前進),所以前端是在右邊。一旦移除了elem3,那麼elem4的接班人(successor,後面跟著的)就會是elem2了

To add or remove an element, we need access to its predecessor in order to update that element’s links.

4:58:00

In a singly linked list there is no easy way to get to an element’s predecessor. For

在單向連結串列是沒有簡便的辦法去取得一個元素的前位元素(前置元素, predecessor)

就是因為這樣,forward_list就另外定義了insert_after,emplace_after,erase_after這樣的成員函式,並且還定義了一個名為before_begin的成員函式來取得「an off-the-beginning iterator」在第一個元素之前的那個位置的迭代器

頁351

這裡的after還是右端的嘛,要移除elem3就在elem2上呼叫erase_after,意指elem3就在elem2之後嘛



觀念題!

可見「sucessor、predecessor」和「after、before」乃至「back、front」是不同的概念,因為「sucessor、predecessor」是針對循序容器(sequential container)單向連結串列(singly linked list)而言的,所以它的advance前端(predecessor)就是這裡的after或back


5:17:00

Table 9.8. Operations to Insert or Remove Elements in a forward_list

表9.8 :在一個forward_list中插入或移除元素的運算

lst.before_begin()

1st. cbefore_begin () 代表緊接在串列開頭前的不存在的元素之迭代器。這個迭代器不能被解參考。cbefore_begin ()會回傳一個 const_ iterator 。

1st.insert_after(p, t)

1st.insert_after(p,n,t)

lst.insert_after(p,b,e)

1st.insert_after (p, il) 在迭代器p所代表的元素之後(after )插入元素。t是一個物件、n是一個計數、b和e是代表個範圍(b與e絕對不可以指向1st)的迭代器,而il是大括號圍起的一個串列。回傳一個迭代器指向最後一個插入的元素。如果該範圍是空的,就回傳p。如果p是off-the-end迭代器,就是未定義。

emplace_after(p, args) 使用在迭代器p所表示的那個元素後建構一個元素。回傳一個迭代器指向那個新元素。如果p是off-the-end迭代器, 就是未定義。

lst.erase_after(p)

lst.erase_after (b,e) 移除在迭代器p所代表的那個元素之後的元素,或移除從迭代器b後那一個元素一直到但不包括e所代表的那個元素這 個範圍的元素。回傳一個迭代器指向最後一個刪除的元素後 的那一個元素,如果沒有這種元素,就回傳off-the-end迭代 器。如果p代表1st中最後一個元素,或者它是off-the-end 迭代器,那就是未定義。



在處理forward_list元素的增減時,要同時顧慮到二個迭代器,一個是要處理的這個元素的迭代器,一個是這個元素左邊一個元素的迭代器(iterator)

頁352

練習9.27

5:43:40

#include<forward_list>

using namespace std;

int main() {

forward_list<int>flst = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89 };

forward_list<int>::const_iterator i = flst.cbegin();

forward_list<int>::const_iterator prev = flst.before_begin();

while( i != flst.cend())

{

if (*i % 2)

{

i=flst.erase_after(prev);

}

else

{

prev = i;

++i;

}

}

}

練習9.28

5:56:33

#include<forward_list>

#include<string>

using namespace std;

void flist(forward_list<string>flstStr,string s1,string s2) {

forward_list<string>::const_iterator i = flstStr.cbegin();

forward_list<string>::const_iterator prev = flstStr.before_begin();

bool flg=false;

while (i != flstStr.cend())

{

if (*i ==s1)

{

i = flstStr.insert_after(i,s2);

flg = true;

}

++i;

++prev;

}

if (!flg) {

flstStr.insert_after(prev, s2);

}

}

int main() {

forward_list<string>flst = { "孫守真","士林區","范仲淹","江蘇省","連橫","臺灣","包拯","安徽省" };

flist(flst, "孫守真1", "任真");

}



9.3.5. Resizing a Container

9.3.5調整一個容器的大小

改動容器大小

resize()運算,元素都是從後面(右邊)增減的:

將容器調小是從後面開始砍元素的

加元素也是由後面加的

6:26:58



6:29:00

Table 9.9. Sequential Container Size Operations

表9.9 :循序容器的大小運算

resize 對 array 無效。

c.resize(n) 重新調整c的大小,讓它有n個元素。如果n<c.size(),多餘的元 素會被捨棄。如果必須加入新的元素,它們會是值初始化的。

c.resize (n, t) 重新調整c的大小,讓它有n個元素。所新增的任何元素都有值t。

WARNING

如果resize縮小容器,那麼迭代器、參考和指標,只要指向的是被刪除的元素, 就會無效化。vector、string或deque有可能無效化所有的迭代器、指標和參考。

若在vector string deque上做resize()運算,很可能容器上所有的迭代器、指標、參考,都會失效

頁353

練習9.29

6:33:19

6:35:20

練習9.30

6:37:00

9.3.6. Container Operations May Invalidate Iterators

容器運算可能使迭代器失效

牽一髮而動全身

6:39:00

只要容器被重新配置記憶體空間,那麼vector、string的容器內的迭代器、指標、參考就會悉數無效

If no reallocation happens, indirect references to elements before the insertion remain valid; those to elements after the insertion are invalid.

只要不是在前端或末端加入元素,deque內所有的元素迭代器、指標、參考就會失效

If we add at the front or back, iterators are invalidated, but references and pointers to existing elements are not.

如果在deque的前端或末端加入元素,那麼迭代器會失效,會指向原來存在的元素的指標與參考,還不會失效

6:47:00

• Iterators, pointers, and references (including the off-the-end and the beforethe- beginning iterators) to a list or forward_list remain valid,

為什麼是逗號?

以上是加入新元素時,迭代器、參考、指標會不會失效的情況

以下是移除元素時:

6:48:30

It should not be surprising that 並不意外when we remove elements from a container,

iterators, pointers, and references to the removed elements are invalidated.

中文版翻成這樣「這點應該不令人驚訝」,您說哪一個比較是中文?

不管是加入元素或移除元素,list和forward_list內的指標、參考、迭代器都會保持其效用/效力

如果不是在首尾兩端移除元素,則deque內的迭代器、指標、參考都會無效

如果是從尾端移除deque的元素,則只有尾端後迭代器(off-the-end iterator)會失效,其他則並不會受影響;若是從前端移除,它們也不會有影響

對於string、vector來說在移除點之前的迭代器、指標、參考一樣有效;而只要有移除元素,尾端後迭代器(off-the-end iterator)都會失效

7:1:10

頁354

對迭代器(iterator)要好好管理:

When you use an iterator (or a reference or pointer to a container element), it is a good idea最好是 to minimize the part of the program during which an iterator must stay valid.

最好是縮小一個迭代器須要保持有效性的範疇

也就是說不要太過度倚賴迭代器的操作,以免出錯

每當對容器有所變更後都當確定迭代器有被重新定位好

尤其對於vector string deque而言,迭代器最有可能會有失效的風險。而list、forward_list就不會失效。

7:8:30

Writing Loops That Change a Container

7:20:00

Insert成員函式總是在指定的位置前插入新元素

7:27:00

Avoid Storing the Iterator Returned from end

意思就是盡量直接用end()成員函式而不是將它指定給一個變數來用。因為end()傳回來的迭代器在容器運算後往往會失效,而若直接調用end()成員函式,則隨時可以更新(refresh)尾端後迭代器(off-the-end iterator)是誰。若是指定end()給一個變數,則只是一種拷貝,是傳值,而不是傳址(pass by reference),end()更新迭代器狀態後這個拷貝的值(變數)並不會連動更新。因為C++程式庫對end()此一成員函式的實作是非常有效率的,所以不必擔心通用它會有什麼overhead(額外負擔或函式成本,參閱前面的頁238行內函式(inline function))

只要是vector或string加入或移除元素都會使尾端後迭代器(off-the-end iterator)失效


deque的話,則只要不是在前端新增或移除元素也一樣

中文版又翻錯:

When we add or remove elements in a vector or string, or add elements or remove any but the first element in a deque, the iterator returned by end is always invalidated失效.

當我們在一個vector或string中新增或移除元素,或在一個deque中移除第一個元素以外的任何元素,end所回傳的元素永遠都會無效化。

end即 .end()成員函式



頁355

7:33:00

可見將函式傳回的值儲存(拷貝)在一個變數中,往往是「優化」程式的一種手段(慣用手法)

7:48:02

9.4. How a vector Grows

9.4 vector的增長方式

vector是怎麼增加/加入元素的



To support fast random access, vector elements are stored contiguously—each element is adjacent to the previous element. Ordinarily, we should not care about how a library type is implemented; all we should care about is how to use it. However, in the case of vectors and strings, part of the implementation leaks into its interface.它的實作,是在它的介面上可以窺出端倪的

為了支援快速的隨機存取,vector元素是以連續的方式儲存,每個元素都與前一個元素相鄰。 一般來說,我們不應該在意一個程式庫型別是如何實作的;我們應該關心的只是如何使用它。 然而,對vector和string來說,有些實作細節會其介面之上洩漏。

8:00:00

deallocate the old memory.

解除配置記憶體空間

頁356

Exercises Section 9.3.6

8:4:20

Word 插入交互參照

Word VBA

留言

熱門文章