千慮一得齋OnLine_觀死書齋-Yahoo/Hexun/Blogger/sina/Xuite

觀死書齋暨Spread、和訊博客全文檢索

2019年10月3日 星期四

C++自修入門實境秀、C++ Primer 5版研讀秀 55/ ~9.3.1. Adding Elements to a Sequential ...





9.2.5. Assignment and swap

9.2.5指定和swap(對調)

c1 = {a,b,c}; // after the assignment c1 has size 3

指定後會改變容器的大小(會縮小不會變大)

If the containers had been of unequal size, after the assignment both containers would have the size of the right-hand operand.

14:00



頁338

Unlike built-in arrays, the library array type does allow assignment. The left-and right-hand operands must have the same type:

不能用中括弧的串列去指定array容器:

array<int, 10> a1 = {0,1,2,3,4,5,6,7,8,9};

array<int, 10> a2 = {0}; // elements all have value 0

a1 = a2; // replaces elements in a1

a2 = {0}; // error: cannot assign to an array from a braced list

因為大小是array型別的一部分,所以要指定的話,必須兩邊一致,所以「same type」即是元素型別和多少是一樣的:

Because the size of the right-hand operand might differ from the size of the left-hand operand, the array type does not support assign and it does not allow assignment from a braced list of values.



Table 9.4. Container Assignment Operations

容器型別關於指定操作的運算

表9.4 :容器的指定運算

c1 = c2 將c1中的元素取代為c2中元素的拷貝。c1和c2必須有相同型別。

c = {a, b, c … } 以初始器串列中元素的拷貝來取代c1中的元素。(對array無效)

swap (c1, c2) 將c1中的元素與c2中的元素互換。c1和c2必須是相同型別。Exchanges elements in cl with those in c2. cl and c2 must be the same type,

c1.swap(c2) swap通常會比從c2到cl拷貝元素還要快很多。

swap is usually much faster than copying elements from c2 to c1.

assign運算對關聯式容器或array來說無效

seq.assign(b,e) 將seq中的元素以迭代器b和e所代表的元素取代。迭代器b和e不 可以指向seq中的元素。

seq.assign(il) 以初始器串列il中的元素取代seq中的元素。

seq.assign(n,t) 以值為t的n個元素取代seq中的元素。

il,應是initializer list之意(果然,見頁344 The versions of insert that take a pair of iterators or an initializer list insert the elements from the given range before the given position:。第55集 7:38:20

seq,即循序容器(sequential container)之縮寫



WARNING:

Assignment related operations invalidate iterators, references, and pointers into the left hand container.

Aside from string they remain valid after a swap, and (excepting arrays) the containers to which they refer are swapped.

和指定assignment ⑦先抓動詞 ⑧找對主詞 相關的運算會使指向左手邊容器的迭代器、參考,以及指標無效化。

不過string在swap之後還是有效,而(除了array外),它們所指的容器會被對調。

1:17:54 中文版又翻錯了!(參見下頁 )這前面講的是assign,後面講的是swap,分屬兩個成員、方法,不可混同!assign後,原迭代器、指標、參考均會失效,但swap後,它們仍然有效,只有在string這樣的容器被swap後才會失效。它們不但在swap後仍然有效,且指向的容器會被對調(除了array這樣的容器例外)。

36:00

Using assign (Sequential Containers Only)

使用assign (僅限循序容器)

這個assign是指方法(循序容器的成員函式)

44:00

頁339

// equivalent to slist1.clear();

// followed by slist1.insert(slist1.begin(), 10, "Hiya!");

list<string> slist1(1); // one element, which is the empty string

slist1.assign(10, "Hiya!"); // ten elements; each one is Hiya !

可見assign方法(成員函式)可以重新定義或調整容器的大小(元素的多少)的

55:30

Using swap

使用 swap方法(即「成員函式」)

其實是對調二個容器內部的資料結構,並不是對其元素的處理

相同型別的容器才能用 swap

After the call to swap , the elements in the two containers are interchanged:

swap (c1, c2)



c1.swap(c2)

第一個應是容器類別的介面函式,而第二種是容器類別型別物件的成員函式(即「方法」)(果然,詳下頁339)



swap is usually much faster than copying elements from c2 to c1.

With the exception of array s, swapping two containers is guaranteed to be fast—the elements themselves are not swapped; internal data structures are swapped.

除了 array這個例外,對調兩個容器的動作保證會很快速,元素本身不會對調,互換的是內部的資料結構。

正是因為只有對調資料結構,所以速度才快

swap時間(耗時)沒有快慢之分:

Note: Excepting array, swap does not copy, delete, or insert any elements and is guaranteed to run in constant time.

其所需的時間是固定的

The fact that elements are not moved means that, with the exception of string, iterators, references, and pointers into the containers are not invalidated. They refer to the same elements as they did before the swap. However, after the swap, those elements are in a different container. For example, had iter denoted the string at position svec1 [3] before the swap, it will denote the element at position svec2[3] after the swap.

Differently from the containers, a call to swap on a string may invalidate iterators, references and pointers.

不同於其他容器→和這些容器不同,在一個string上呼叫swap 會使迭代器、參考和指標無效化。

用into大概是因為若用to,就成了指向容器本身,而若用「in」則成了在容器中的元素本身,都不是。所以用into,即指向容器中的元素的指標、參考與迭代器。

1:36:00

Unlike how swap behaves for the other containers, swapping two array s does exchange the elements.

其他容器swap只是對調容器,並不會動到元素;而array這樣的容器swap後是真的會動到它的元素。因此,對調(swap)兩個array這樣的容器所需的時間,就看它們的元素多少來決定

只有string例外,swap後,所有和元素有關的,諸如迭代器、指標、參考都仍有效,且仍然指向同一個「東西」(元素)。這裡是專就array談,但其他容器,除了string也類似:

After the swap , pointers, references, and iterators remain bound to the same element they denoted before the swap . Of course, the value of that element has been swapped with the corresponding element in the other array .

所以array和其他容器在做swap的時候也類似,只是因為實際上在行動元素,所以花的時間,就不像其他容器是恆定的了。

1:49:50

In the new library, the containers offer both a member and nonmember version of swap .

這就是我們之前提到的介面函式(nonmember)和成員函式的swap

非成員的swap在泛型程式中最為重要。就習慣而言,最好是使用非成員版的swap。

習慣上,最好使用swap的非成員版本。(google翻譯)

頁340

練習9.14

1:55:59

2:23:20 忘了按暫停!

C式的字元字串,其實就是char的陣列(字元陣列(character arrays)

C-Style的字元字串

Strings that follow this convention are stored in character arrays and are null terminated.(頁122)

#include<vector>

#include<string>

#include<list>

using namespace std;



int main() {

vector<string > vecs{"孫守真","文天祥","顧炎武"};

char ca[] = "abcde";

list<char*> lstcp(10,ca);

vecs.assign(lstcp.cbegin(),lstcp.cend());//相當於下式

//vecs.clear(); vecs.insert(vecs.cbegin(), lstcp.size(), *(lstcp.begin()));

}

or

const char ca[] = "abcde";

list<const char*> lstcp(10,ca);

4:8:38

9.2.6. Container Size Operations

和容器大小相關的運算有三個成員函式 size()、empty() and max_size():

and max_size returns a number that is greater than or equal to the number of elements a container of that type can contain.

也就是那個型別容器能容下的最大數,應該和Visual Studio 2019 中所顯示時capacity()方法(成員函式)是不一樣的

只有 forward_list只提供了兩種關於容器大小的運算(沒有.size())。

9.2.7. Relational Operators

這一段是談各容器間的大小關係是如何計算的

4:16:59

關係運算子

所有的容器(包括關聯式容器(associative container)都支援相等性運算子(==、!=),而除了無序的關聯式容器(unordered associative containers)外,都支援關係運算子(relational operator:>,>=,<,<=)

關係運算子的兩端運算元一定要同型別的容器,且有同型別的元素

容器與元素型別均同才能作關係比較

逐對比較(pairwise comparison)

就是一個元素與其對應位置的元素(corresponding element)作比較

初始子序列(initial subsequence)

4:32:20

意指一開始的元素(即[0])就相同的子序列

vector<int> v1 = { 1, 3, 5, 7, 9, 12 };

vector<int> v2 = { 1, 3, 9 };

vector<int> v3 = { 1, 3, 5, 7 };

vector<int> v4 = { 1, 3, 5, 7, 9, 12 };

v1 < v2 // true; v1 and v2 differ at element [2]: v1[2] is less than v2[2]

v1 < v3 // false; all elements are equal, but v3 has fewer of them;

v1 == v4 // true; each element is equal and v1 and v4 have the same size()

v1 == v2 // false; v2 has fewer elements than v1

v3就是v1的初始子序列(initial subsequence)



頁341

Relational Operators Use Their Element’s Relational Operator

關係運算子會使用它們元素的關係運算子

⑧找對主詞

Containers’ Relational Operators Use Their Element’s Relational Operator

只能在其元素型別有適當的比較運算子存在時才能利用容器的比較運算子(關係運算子(relational operator))

因為比較兩個容器是一定要用到元素比較的!

物有本末,事有終始;知所先後,則近道矣

For example, the Sales_data type that we defined in Chapter 7 does not define either the == or the < operation. Therefore, we cannot compare two containers that hold Sales_data elements:


4:45:07

練習9.15

#include<vector>

#include<iostream>

using namespace std;

bool equal2containers(vector<int>veci1, vector<int> veci2) {

if (veci1.size() == veci2.size())

{

vector<int>::size_type sz{0};

for(int var : veci1){

if (var != veci2[sz])

return false;

++sz;

}

return true;

}

return false;

}

int main() {

vector<int>veci1{2,3,4,4,5};

vector<int>veci2{2,3,4,4,5};

cout<<equal2containers(veci1, veci2)<<endl;

}



4:55:50

練習9.16

#include<string>

#include<vector>

#include<list>

#include<iostream>

using namespace std;

string compareElements(list<int>lsti, vector<int> veci) {

if (lsti.empty() || veci.empty()) return "請傳入有效容器!";

vector<int>::size_type sz{0};

for (int var : lsti) {

if (var > veci[sz])

return "list 大於 vector";

else if (var < veci[sz])

{

return "list 小於 vector";

}

else

{

++sz;

if (sz > (veci.size() - 1))

if(lsti.size() !=veci.size())

return "list 元素多於 vector,且vector是list的初始子序列(initial subsequence)";

else

return "list 等於 vector";

}

}

if (lsti.size()<veci.size())

{

return "list 是 vector的初始子序列(initial subsequence)";

}

else

return "list 等於 vector";

}



int main() {

list<int>lsti{2,3,4,4,5};

vector<int>veci2{2,3,4,4,5};

cout<<compareElements(lsti, veci2)<<endl;

}

5:24:50

練習9.17

1)不能是無序的關聯式容器(unordered associative containers)

2)容器型別與元素型別必須皆一致

5:28:50

9.3. Sequential Container Operations

9.3循序容器的運算

循序容器(sequential container)與關聯式容器(associative container)的不同在於對其元素組織布排的方式:

The sequential and associative containers differ in how they organize their elements. These differences affect how elements are stored, accessed, added, and removed.

接下來都將會專門討論循序容器的運算

5:33:50

9.3.1. Adding Elements to a Sequential Container

在一個循序容器中加入元素

除了array這樣的容器,程式庫型別的容器都可以在執行階段動態地改變容器的大小:

Excepting array , all of the library containers provide flexible memory management. We can add or remove elements dynamically changing the size of the container at run time.

vector、string如果不是在尾端加入元素,deque若不是在首尾兩端加入元素,那麼就必須行動其他被影響的元素,來容納新加入的元素。因此就有效能問題了。

對vector或string容器加入元素也可能需要整個物件重新配置記憶體空間。所有的元素都必須從原位,移動到新的記憶體位置

頁342

6:13:30 6:17:33

Visual Studio 2019 與 github的操作

刪除本地冗餘的檔案,再push到github,GitHub上才不會在每個分支(branch)上殘留太多複不必要的檔案,而需要的檔案,仍會保留在相對應的鍛(branch)中,只要切換branch就會與Github上的同步

Using push_back

6:29:49 6:31:00

Because string is just a container of characters, we can use push_back to add characters to the end of the string :

注意:是加「字元(字符,character)」不是加「字串(string)」

void pluralize(size_t cnt, string &word) {

if (cnt > 1) word.push_back(' s '); // same as word += 's'

}



6:41:00

Key Concept: Container Elements Are Copies

When we use an object to initialize a container, or insert an object into a container, a copy of that object’s value is placed in the container, not the object itself.

關鍵概念:容器元素是拷貝→重要觀念:容器元素只是副本/複本

不是它自己放到容器中,而是它的版本放進容器中,因此,如果有對該物件(東西)的參考、指標等等,一樣沒變。

Using push_front

頁343

6:46:30

push_front只適用在list forward_list deque

deque不管頭尾插入元素都一樣快

如果不在頭尾兩端插入新元素,則deque和vector一樣,都會較為耗時、效能較低:

As with vector, inserting elements other than at the front or back of a deque is a potentially expensive operation.

就跟vector —樣,在一個deque的前端或後端以外的地方插入元素很可能會是昂貴的運算。

昂貴的運算→這是哪一國的中文?



5:40:22

Table 9.5. Operations That Add Elements to a Sequential Container

表9.5 :新增元素到一個循序容器的運算

這些運算會改變容器的大小;array沒有支援它們。

forward_list 有特殊版本的 insert 和 emplace,請參閱 § 9.3.4。

push_back 和emplace_back 對 forward_list 來說無效。

push_front 和 emplace_front 對 vector 或 string 來說無效。

c.push_back(t) c.emplace_back (args) 在c的後端(back)創建具有值t的元素或從args建構出來的元素。回傳void 。

c.push_front(t) c.emplace_front(args) 在c的前端(front)創建具有值t的元素或從args建構出來的元素。回傳void 。

c. insert(p,t) c.emplace(p, args) 在迭代器p所代表的元素前創建一個具有值t的元素或從args 建構出來的元素。回傳一個迭代器指向被新增的那個元素。Creates an element with value t or constructed from args before the element denoted by iterator p. Returns an iterator referring to the element that was added.

c.insert(p,n,t) 在迭代器p所表示的元素前插入n個具有值t的元素。回傳一個 迭代器指向所插入的第一個元素;如果n為零,就回傳p。

c.insert(p,b,e) 在迭代器p代表的元素前插入迭代器b和e表示的範圍中的元素。 b和e不可以指向c中的元素。回傳一個迭代器指向所插入的第 一個元素;如果範圍是空的,就回傳p。

c.insert(p,il) il(initializer list)是由大括號圍起的元素值串列。在迭代器p所代表的元素前 插入所給定的值。回傳一個迭代器指向第一個插入的元素;如果 串列是空的,就回傳P 。



Returns an iterator referring to the element that was added.

回傳一個迭代器指向被新增的那個元素→回傳一個指向新增元素的迭代器。



WARNING:

新增元素到一個vector、string或deque可能會使容器所有既存的迭代器、參考和指標都無效化。

無效化→失效

言下之意難道是list和forward_list不會失效?

7:1:20

Adding Elements at a Specified Point in the Container

The insert members are supported for vector , deque , list , and string . forward_list provides specialized versions of these members that we’ll cover in § 9.3.4 (p. 350 ).

因為要指定位置(插入點):

Each of the insert functions takes an iterator as its first argument.

所有可以是迭代器的位置都可以。

會從這個迭代器引數指向的位置前插入元素

slist.insert(iter, "Hello!"); // insert "Hello!" just before iter

頁344

即使某些容器是不提供push_front的運算,但是若是在它們身上用instert,也沒有類似的限制(即可以在其前端插入新元素):

Even though some containers do not have a push_front operation, there is no similar constraint on insert .

然而即使可以在vector前端insert新元素,但也別忘了,只要不是在vector的尾端插入新元素,其效能都是很差的

7:19:00

Warning: It is legal to insert anywhere in a vector, deque , or string . However, doing so can be an expensive operation.

在一個vector、deque或string的任何地方插入元素都是合法的。然而,這麼做 有可能會是昂貴的運算。

雖然在vector、deque、string的任何一個位置插入新元素都是可以的,但只要不是在尾端(vector string)或在首尾兩端(deque)插入,都須付出一定的/可觀的代價

7:23:20

Inserting a Range of Elements

insert引數,在最前面的迭代器引數後接的是一個類似容器建構器的引數:

The arguments to insert that appear after the initial iterator argument are analogous to the container constructors that take the same parameters.

這個引數是和那種建構器一樣,帶了一個元素量和一個元素值的兩個引數(參數)

The version that takes an element count and a value adds the specified number of identical elements before the given position:

The version這種版本的insert,會在第一個引數迭代器指定的位置前插入這樣的元素量與元素值。



7:45:00

insert如果帶有要插入元素範圍的引數,那麼就會回傳迭代器。若插入成功,則回傳插入範圍元素的第一個元素的迭代器,否則就回傳insert的第一個引數的迭代器。

7:48:10

頁345

Using the Return from insert

怎麼運用insert回傳的值

list<string> 1st; auto iter = 1st.begin(); while (cin >> word) iter = 1st.insert(iter, word); // same as calling push_front

利用insert的回傳迭代器,我們可以再代入insert成員函式中,作為第一個引數,在同一個指定處而插入元素

list<string> 1st;

auto iter = 1st.begin();

while (cin >> word)

iter = 1st.insert(iter, word); // same as calling push_front

7:57:40

Using the Emplace Operations

新標準引進了3個新成員:

emplace

emplace_front

emplace_back

這三個是建構新元素,而不是拷貝元素

這樣的運算,是為了與insert、push_front、push_back相對應的

當調用emplace這組操作時,是傳遞了想要建構的元素型別的建構器所需的引數:

When we call an emplace member, we

pass arguments to a constructor for the element type.

The emplace members use those arguments to construct an element directly in space managed by the container.

emplace成員使用那些引數直接在容器所管理的空間中建構一個元素。

「建構、構建」皆可

// construct a Sales_data object at the end of c

// uses the three-argument Sales_data constructor

c.emplace_back("978-0590353403", 25, 15.99);

// error: there is no version of push_back that takes three arguments

c.push_back("978-0590353403", 25, 15.99);

// ok: we create a temporary Sales_data object to pass to push_back

c.push_back(Sales_data("978-0590353403", 25, 15.99));



emplace這組成員函式的引數,是看要建構的元素型別而定的,而不是固定的哪些引數:

The arguments to an emplace function vary depending on the element type.

其引數必須匹配該元素型別的某個建構器

其引數當然就必須要和要建構的元素型別它的建構器所需的引數能符合

頁346

8:18:55

// iter refers to an element in c, which holds Sales_data elements

c.emplace_back(); // uses the Sales_data default constructor,因為預設建構器是不帶引數的

c.emplace(iter, "999-999999999"); // uses Sales_data(string):在做函式匹配(function matching)

// uses the Sales_data constructor that takes an ISBN, a count, and a price

c.emplace_front("978-0590353403", 25, 15.99);

練習9.18

8:24:35

#include<string>

#include<deque>

#include<iostream>

using namespace std;

void read() {

string word;

deque<string> deqstr;

while (cin>>word)

{

deqstr.push_back(word);

}

for(deque<string>::const_iterator iter=deqstr.cbegin();iter != deqstr.cend();++iter)

cout << *iter << endl;

}



int main() {

read();

}

8:42:38

練習9.19

#include<string>

#include<list>

#include<iostream>

using namespace std;

void read() {

string word;

list<string> deqstr;

while (cin>>word)

{

deqstr.push_back(word);

}

for(list<string>::const_iterator iter=deqstr.cbegin();iter != deqstr.cend();++iter)

cout << *iter << endl;

}



int main() {

read();

}

8:50:43

練習9.20

#include<deque>

#include<list>

using namespace std;

void copyElements() {

list<int>lsti{1,3,2,4,7,8,9,10,111};

deque<int>deqiEven;

deque<int>deqiOdd;

for (list<int>::const_iterator iter = lsti.cbegin(); iter != lsti.cend(); ++iter)

{

int i = *iter;

if (i % 2 == 0)

{

deqiEven.push_back(i);

}

else

deqiOdd.push_back(i);

}

}



int main() {

copyElements();

}

9:1:10

練習9.21

#include<string>

#include<vector>

#include<iostream>

using namespace std;

int main() {

string word;

vector < string> lst;

auto iter = lst.begin();

while (cin >> word)

iter = lst.insert(iter, word); // same as calling push_front



/*string word;

list < string> lst;

auto iter = lst.begin();

while (cin >> word)

iter = lst.insert(iter, word); // same as calling push_front

*/

}

9:13:29

練習9.22

#include<string>

#include<vector>

#include<iostream>

using namespace std;

int main() {

int some_val=10;

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

vector<int>::iterator iter = iv.begin(),

mid = iv.begin() + iv.size() / 2;

while (iter != mid)

{

if (*iter == some_val)

{

iter = iv.insert(iter, 2 * some_val);//在vector插入元素後,它元素的迭代器會失效,所以「mid」 也會失效

//break;//此下二行均可無誤地離開迴圈

mid = iv.begin() + iv.size() / 2;

}

++iter;

}

}

沒有留言:

張貼留言