C++自修入門實境秀、C++ Primer 5版研讀秀 57/ ~ v9循序容器 9.5.2. Other Ways to Change a ...





練習9.31

#include<forward_list>

#include<list>

#include<vector>

using namespace std;

int main() {

// silly loop to remove even-valued elements and insert a duplicate of odd-valued elements

vector<int> vi = { 0,1,2,3,4,5,6,7,8,9 };

list<int> lsti = { 0,1,2,3,4,5,6,7,8,9 };

forward_list<int> flsti = { 0,1,2,3,4,5,6,7,8,9 };

auto iter = vi.begin(); // call begin, not cbegin because we're changing

auto iterl = lsti.begin();

auto iterf = flsti.begin();

auto iterfprev = flsti.before_begin();

while (iter != vi.end()) {

if (*iter % 2) {

iter = vi.insert(iter, *iter); // duplicate the current

iterl = lsti.insert(iterl, *iterl); // duplicate the current

iterf = flsti.insert_after(iterf, *iterf); // duplicate the current element

iter += 2; // advance past this element and the one inserted before it

++iterl; ++iterl;//list不能隨機存取元素,只能迭代巡覽,所以其迭代器沒有整數加減運算

iterfprev=iterf; ++iterf;//forward_list是沒有--(遞減)運算,不能點頭的,只能advance(向前forward)

}

else

{

iter = vi.erase(iter); // remove even elements

iterl = lsti.erase(iterl); // remove even elements

iterf = flsti.erase_after(iterfprev); // remove even elements

// don't advance the iterator; iter denotes the element after the one we erased

}

}

}

39:15

練習9.32

#include<vector>

using namespace std;

int main() {

// silly loop to remove even-valued elements and insert a duplicate of odd-valued elements

vector<int> vi = { 0,1,2,3,4,5,6,7,8,9 };

auto iter = vi.begin(); // call begin, not cbegin because we're changing

while (iter != vi.end()) {

if (*iter % 2) {

//iter = vi.insert(iter, *iter); // duplicate the current

iter = vi.insert(iter, *iter++); // duplicate the current

++iter ; // advance past this element and the one inserted before it

}

else

{

iter = vi.erase(iter); // remove even elements

// don't advance the iterator; iter denotes the element after the one we erased

}

}

}

50:10

練習9.33

#include<vector>

using namespace std;

int main() {

vector<int>v = { 1,2,3,4,5 };

// disaster: the behavior of this loop is undefined

auto begin = v.begin(),

end = v.end(); // bad idea, saving the value of the end iterator

// safer: recalculate end on each trip whenever the loop adds/erases elements

while (begin != v.end()) {

// do some processing

++begin; // advance begin because we want to insert after this element

//begin = v.insert(begin, 42); // insert the new value

v.insert(begin, 42); // insert the new value

++begin; // advance begin past the element we just added

//插入元素(尤其vector string deque)begin()回傳的迭代器可能會失效

//在此例中,begin失效了

}

}

1:5:55

練習9.34

#include<vector>

using namespace std;

int main() {

vector<int>vi = { 1,2,3,4,5 };

auto iter = vi.begin();

while (iter != vi.end())

{

if (*iter % 2)

{

iter = vi.insert(iter, *iter);

++iter;

}

++iter;

}

}



1:25:00

Members to Manage Capacity

負責管理容器容量的成員

頁357

Table 9.10. Container Size Management

表9.10 :容器的大小管理

shrink—to—fit 僅對 vector、string 和 deque 有效。 capacity 和 reserve 只對 vector 和 string 有效。

c.shrink_to_fit() 請求將 capacity ()降到等於 size ()。

c.capacity () 必須重新配置前,c可以有的元素數。

c.reserve (n) 為至少n個元素配置空間。

想把多餘的記憶體空間騰出來,就須用shrink_to_fit(),不能用reserve(),然而shrink_to_fit就像inline一樣,只能是要求,不能是勒令:

As a result, a call to reserve will never reduce the amount of space that the container uses. Similarly, 同樣的the resize members (§ 9.3.5 , p. 352 ) change only the number of elements in the container, not its capacity.

可見在vector這類的容器,其操作方式是記憶體空間與元素是獨立存在,互不相屬、相連的

capacity才與記憶體空間的管理(配置 allocate)有關,而 size只與元素數量有關,記憶體空間的管理無關



1:48:50

capacity and size

capacity(額度、額定額度)就是負荷量、承載量,能夠接受多少的元素。就像信用額度,是不必花到那麼多錢也;但最多只能花到那麼多錢。

size則是實際已存在的元素數量

頁358

2:00:40

看誰翻得比較好:

The details of how much excess capacity is allocated vary by implementations of the

library.

會多配置多少空間的相關細節,將隨著程式庫的實作而變。

→至於究竟會配置多少額外的記憶體空間,是由程式庫的實際運作來決定

∴ 中文程度還能小看、還敢輕視嗎?

視覺上,我們可以把ivec目前的狀態想成是

視覺上→具象化(具體形象化)

2:14:50 可見reserve()是勒令,不是請求(乞求)。而shrink_to_fit()則是;ask、request只是一個希望、希冀,而未必能夠滿願(頁359) 2:25:00

In fact, as long as no operation exceeds the vector’s capacity, the vector must not reallocate its elements.

事實上,只要沒有運算超出vector的容量,vector就必定不能重新配置其元素。

必定不能→決定不會or一定不會、絕對不會

頁359

Note:

Each vector implementation can choose its own allocation strategy. However, it must not allocate new memory until it is forced to do so.

每個vector實作都能挑選自己的配置策略。然而,除非絕對必要,不然它必定不能配置新的記憶體。

和前面一樣:必定不能→決定不會or一定不會、絕對不會

2:35:00

Every implementation is required to follow a strategy that ensures that it is efficient to use push_back to add elements to a vector. Technically speaking, the execution time of creating an n-element vector by calling push_back n times on an initially empty vector must never be more than a constant multiple of n.

每個實作都被要求得遵循能讓push_back有效率新增元素到vector的策略。從技術上來說,就是在一個最初為空的vector上呼叫push_back n次來創建一個n元素的vector所需的執行時間永遠都不能超過n的常數倍。

應該是說用push_back來創建/指定/拷貝n個元素的vector是不能比配置n個變數值的時間要慢上/超過一倍(一個恆定的倍數a constant multiple。即有時也要快,不能老是那麼長時間)。所以此句:

the execution time of creating an n-element vector by calling push_back n times on an initially empty vector must never be more than a constant multiple of n.

應是下句的承前省略:

the execution time of creating an n-element vector by calling push_back n times on an initially empty vector must never be more than a constant multiple of the execution time of n.

2:42:11

練習9.35

capacity()指「可」容納的(元素)

size()指「已」納入的(元素)

練習9.36

2:45:10

疑鄰竊斧,訂正《列子》引文「谷」誤轉作「穀」

Impossible!



練習9.37

array不能調整大小(size)

list和forward_list不提供隨機存取元素,也沒必要capacity(因為list它們不需要重劃(重新配置)記憶體空間)

list循序地存取元素,就不需要在記憶體相鄰的位置儲存各個元素

練習9.38

3:17:10

#include<vector>

#include<iostream>

using namespace std;

int main() {

vector<int>vi ;

cout << vi.size() << '\t' << vi.capacity()<<endl;//0 0

vi = { 1,2,3,4,5 };

cout << vi.size() << '\t' << vi.capacity()<<endl;//5 5

for (vector<int>::size_type i =0;i != 20;++i)

{

vi.push_back(i);

}

cout << vi.size() << '\t' << vi.capacity()<<endl;//25 33

vi.reserve(50);

cout << vi.size() << '\t' << vi.capacity()<<endl;//25 50

vi.resize(11);

cout << vi.size() << '\t' << vi.capacity()<<endl;//11 50

vi.shrink_to_fit();

cout << vi.size() << '\t' << vi.capacity()<<endl;//11 11

}

練習9.39

3:26:59

#include<vector>

#include<iostream>

using namespace std;

int main() {

vector<string> svec;

svec.reserve(1024);//capacity()=1024

string word;

while (cin >> word)

svec.push_back(word);//word數量不大於capacity(1024),就不會重新配置記憶體

svec.resize(svec.size() + svec.size() / 2);//預先配置值初始化的元素(類似程式庫的實作),需要用到時,改變指定位置上的元素值就好了

//可以用記憶體空間來換取執行的效能。因為reserve capacity可能在閒置過久後被系統剔掉

}



練習9.40

3:50:22

可見要注意消耗記憶體的問題,及效能問題

頁360

9.5. Additional string Operations

4:1:33

string有許多專用的運算是其他循序容器(sequential container)所沒有的

4:8:50

9.5.1. Other Ways to Construct strings

4:13:00

Table 9.11. Additional Ways to Construct strings

4:17:00

表9_11 :建構string的其他方式

n、len2與pos2全都是無號值

string s (cp, n) ; s是cp所指的陣列中的前n個字元的一個拷貝。那個陣列 至少必須有n個字元。

string s (s2, pos2) ; s是s2這個string中從索引pos2開始的字元的一個拷貝。如果pos2 > s2.size(),就是未定義。

string s(s2, pos2, len2); s是s2從索引位置pos2開始的len2個字元的一個拷貝。

如果pos2>s2.size()就是未定義。不管len2的值為 何,最多只會拷貝s2.size() - pos2個位元°

中文版「s2」誤作「s」。cp就是 const pointer

null-terminated

null尾綴的字串

const char *cp = "Hello World!!!"; // null-terminated array

char noNull[] = {'H', 'i'}; // not null terminated

string s1(cp); // copy up to the null in cp; s1 == "Hello World!!!"

string s2(noNull,2); // copy two characters from no_null; s2 == "Hi"

string s3(noNull); // undefined: noNull not null terminated

string s4(cp + 6, 5);// copy 5 characters starting at cp[6]; s4 == "World"

string s5(s1, 6, 5); // copy 5 characters starting at s1[6]; s5 == "World"

string s6(s1, 6); // copy from s1 [6] to end of s1; s6 == "World!!!"

string s7(s1,6,20); // ok, copies only to end of s1; s7 == "World!!!"

string s8(s1, 16); // throws an out_of_range exception

4:30:44

cp+6 就是指標advance移動6個位置

而cp是指向cp字元陣列(character arrays)的指標(即其第一個元素的指標(pointer))

len2(長度)是可以長過字串或字元陣列(character arrays)大小,但會被忽略。

string s2 (noNull, 2 ) ; // 從 no_null 拷貝兩個字元;s2 == H

no_null應是「noNull」之訛

undefined有些時候要翻成「沒有意義」或「毫無意義」才是!

4:43:20

頁361

4:48:10

The substr Operation

substr運算

子字串的運算

string s4 = s.substr(6, 11); // s3 = world

s3當作s4,中文版也沒更正

4:56:50

Table 9.12. Substring Operation

表9.12 :子字串運算

s.substr(pos, n) 回傳一個string包含s從pos開始的n個字元。pos預設是0。 n預設是會使程式庫拷貝s中從pos開始的所有字元的一個值。

若沒有傳遞引數給substr()就會回傳原來的字串的複本(拷貝)

練習9.41

4:59:30

#include<vector>

#include<string>

#include<iostream>

using namespace std;

int main() {

vector<char> cvec{'g','o','o','d'};

string s,sp;

for (char a : cvec){

s += a;

sp.push_back(a);//這兩種方式都可以

}

cout << s <<'\t'<<sp<< endl;

}

5:13:20

練習9.42

#include<string>

#include<fstream>

#include<iostream>

using namespace std;

int main() {

char a;

ifstream ifstrm("G:\\我的雲端硬碟\\!temp\\new.txt");

string s,sp;

//s.reserve(960010);//有沒有reserve效能似乎差不多

sp.reserve(960010);

while (ifstrm>>a){

//s += a;

sp.push_back(a);//這兩種方式都可以,效能好像也差不多。

}

cout << s <<'\t'<<sp<< endl;

}

5:26:30

9.5.2. Other Ways to Change a string

改變字串值的其他方法

頁362

string在運算時索引值是不含末端的null在內的。用size()來當索引值,不-1,來指向末尾的null這個字元(字符,character)

The number of characters we request must be less than or equal to the number of characters (excluding the null terminator) in the array to which cp points.

When we call insert on s , we say that we want to insert the characters before the (nonexistent) element at s[size()] .

5:56:10

The append and replace Functions

The append operation is a shorthand way of inserting at the end:

The replace operations are a shorthand way of calling erase and insert :

6:2:13 6:6:00

// starting at position 11, erase three characters and then insert "5th"

s2.replace(11, 3, "5th");



s.replace(11, 3, "Fifth"); // s == "C++ Primer Fifth Ed."

In this call we remove three characters but insert five in their place.

頁363

Table 9.13. Operations to Modify strings

表9.13 :修改string的運算

s.insert (pos,args) 在pos前面插入args所指定的字元。pos可以是一個索引或 一個迭代器。接受索引的版本會回傳對s的一個參考;接 受迭代器的版本會回傳一個迭代器,代表所插入的第一個 字元。

s.erase(pos, len) 從位置pos開始的地方移除len個字元。如果len被省略, 就會移除從pos到s尾端的字元。回傳對s的一個參考。

s.assign(args) 依據args移除s中的字元。回傳對s的一個參考。

s.append (args) 將args附加到s。回傳對s的一個參考。

s.replace(range, args) 從s移除range這個範圍的字元,並以args所構成的字元 取代它們。range不是一個索引,就是一個長度或指向s中 的一對迭代器。回傳對s的一個參考。

args可以是下列之一;append和assign可以使用所有的形式 str必須跟s不同,而迭代器b與e不可以指向s

str string str 。

str、pos、len str從pos開始的len個字元。

cp、len cp所指的字元陣列的len個字元。

cp 指標cp所指的null-terminated陣列。

n、c 字元c的n個拷貝。

b、e 迭代器b與e所構成的範圍。

初始器串列 圍在大括號中,以逗號區隔的字元串列。

replace和insert的args取決於range和pos是如何指定的°

replace replace insert insert args可以是

(pos 、 len 、 args) (b 、 e 、 args) (pos 、 args) (iter 、 args)

yes yes yes no str

yes no yes no str 、 pos 、 len

yes yes yes no cp、len

yes yes no no cp

yes yes yes yes n 、 c

no yes no

no yes b2 、 e2

no yes yes 初始器串列



中文版又翻錯了:

s.assign(args) 移除→取代

s.replace(range, args)此式的引數與前例亦有所不同

6:30:07

The Many Overloaded Ways to Change a string

變更一個string的許多重載方式

6:35:30

頁364

留言

熱門文章