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

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

2019年9月29日 星期日

C++自修入門實境秀、C++ Primer 5版研讀秀 53/ ~ 9.2.4. Defining and Initializing a Co...





頁330

Table 9.2. Container Operations

表9.2 :容器運算

表9.2 :容器運算

型別別名

iterator 此容器型別的迭代器(iterator)之型別

const_iterator 可以讀取但不能更改其元素的迭代器型別

size_type 大到足以容納此容器型別之容器最大可能大小的無號整數型別

difference_type 大到足以容納兩個迭代器之間差距的有號整數型別

value_type 元素型別

reference 元素的lvalue型別;value_type&的同義詞

const_reference 元素的 const lvalue 型別 (即 const value_type& )

建構

C c; 預設建構器,空的容器(array,參閱§9.2.4)

C c1 (c2); 將c1建構為c2的一個拷貝

C c(b, e); Copy elements從迭代器b和e所表示的範圍拷貝元素(對

C c {a, b, c……}; array來說無效)

串列初始化c

指定和對調

c1 = c2 將c1中的那些元素換成c2中那些

c1 = {a, b, c ……} 將cl中的那些元素換成串列中的那些(對array來說無效)

a.swap(b) 將a中的元素與b中的那些對調

swap(a, b) 等同於a.swap(b)

大小

c.size () c中的元素數(對forward_list來說無效)

c.max_size() c能夠存放的最大元素數

c.empty() 如果c有任何元素,就為false,否則就為true

新增或移除元素(對array來說無效)

注意:這些運算的介面會隨著容器型別而變

c.insert(args) 將args所指定的元素拷貝到c

c.emplace(inits) 使用inits在c中建構一個元素

c.erase(args) 移除args所指定的元素

c.clear() 從c移除所有元素,回傳void

相等性和關係運算子

==、!= 相等性,對所有的容器型別都有效

<、<=、>、>= 關係運算(對無序的關聯式容器無效)

獲取迭代器

c.begin() 、c.end() 回傳指向c中第一個元素的迭代器,以及指向超出最後一個元

c.cbegin() 、c.cend() 素一個位置處的迭代器 回傳const_iterator

可反向的容器額外的成員(對forward_list無效)

reverse_iterator 以相反順序定址元素的迭代器

const_reverse_iterator 無法寫入元素的反向迭代器

c.rbegin{) 、c.rend{) 回傳迭代器指向c中最後一個元素,以及超過第一個元素一個

c.crbegin() 、c.crend() 位置處的迭代器

const_reverse_iterator

31:20

頁331

9.2.1. Iterators

跟容器一樣,迭代器(iterator)也有一個共通/共用的介面

中文版又翻錯了:

all the iterators on the standard container types let us access an element from a container, and they all do so by providing the dereference operator.

在標準容器型別上的所有迭代器都能讓我們存取容器的元素,而它們都是提供差距運算子(dereference operator)來這麼做。

→解參考(dereference)

解參考運算子



With one exception, the container iterators support all the operations listed in Table 3.6 (p. 107 ). The exception is that the forward_list iterators do not support the decrement ( -- ) operator. The iterator arithmetic operations listed in Table 3.7 (p. 111 ) apply only to iterators for string, vector, deque , and array . We cannot use these operations on iterators for any of the other container types.

53:20

Iterator Ranges

迭代器範圍

對標準程式庫而言,迭代器範圍是很重要的基本概念

This element range is called a left-inclusive interval . The standard mathematical notation for such a range is

[ begin, end)

左包含區間(left-inclusive interval)



1:6:30

Requirements on Iterators Forming an Iterator Range

能夠構成一個迭代器範圍的迭代器的必要條件:

Two iterators, begin and end , form an iterator range, if

• They refer to elements of, or one past the end of, the same container, and

• It is possible to reach end by repeatedly incrementing begin . In other words也就是說, end must not precede begin . Warning

The compiler cannot enforce these requirements. It is up to us 就看我們自己to ensure that our programs follow these conventions.

頁332

Programming Implications of Using Left-Inclusive Ranges

使用左包含範圍對程式設計的影響

1:12:36

We can increment begin some number of times until begin == end

•我們可以遞增begin數次,直到begin == end

我們可以不斷地遞增begin直到它和end相等、重疊為止。

這裡的some是不設限、無限定的意思。

或翻成「一直」也可

翻成「數」次就太呆板(板滯)了。

雖然沒翻錯(立於不敗之地)但也不道地、不傳神。下文:

because the loop body increments begin, we also know the loop will eventually terminate.

loop也可證此處就有不斷、一直的意思



we know that it is safe to dereference begin because begin must refer to an element.

1:29:00 可見參考、指標、迭代器,幾乎可說是三位一體的東西

練習9.3

1:38:40

1:40:22

練習9.4

1:57:00

#include<vector>

#include<iostream>



using namespace std;

bool main() {

vector<int> veci{ -3,-2,-1,0,1,2,3,4,5 };

int i = 2;

vector<int>::const_iterator beg = begin(veci) + 1;

vector<int>::const_iterator ed = end(veci) - 3;

while (beg != ed)

{

if (*beg == 2)

{

cout << true << endl;

return true;

}

++beg;

}

cout << false << endl;

return false;

}

練習9.5

#include<vector>

#include<iostream>



using namespace std;

vector<int>::const_iterator main1() {

static vector<int> veci{ -3,-2,-1,0,1,2,3,4,5 };

int i = 2;

static vector<int>::const_iterator beg = begin(veci) + 1;

static vector<int>::const_iterator ed = end(veci)-4;

//左包含區間或左包含範圍是不包括end指標所指之元素的,所以即使end指向2,在此例也一樣屬於未找到也

while (beg != ed)

{

if ((*beg) == 12)

{

cout << "找到了!" << endl;

return beg;

}

++beg;

}

cout << "找不到!" << endl;

return beg;

}



int main(){

cout<< *main1()<<endl;

}



練習9.6

2:13:30

條件式是不成立的,因為:

列於表3.7中的迭代器算術運算僅適用於string、vector、 deque與array的迭代器。我們無法把這些運算用在其他任何容器型別的迭代器上。(頁331)

#include<list>

#include<iostream>



using namespace std;



int main(){

list<int> lst1{2,3,4,11,333,3331};

list<int>::iterator iter1 = lst1.begin(),

iter2 = lst1.end();

while (iter1 != iter2) /* ... */

{

cout << *iter1 << endl;

++iter1;

}

}

2:25:59

9.2.2. Container Type Members

容器的型別成員(type member)

9.2.2容器型別成員

2:33:00

頁333

2:35:00

可以在不知情的情況下使用容器元素的型別:

The remaining type aliases let us use the type of the elements stored in a container without knowing what that type is. If we need the element type, we refer to the container’s value_type . If we need a reference to that type, we use reference or const_reference . These element-related type aliases are most useful in generic programs, which we’ll cover in Chapter 16 .

2:38:50

要用這些型別成員,就必須「具名」稱述(引用):

To use one of these types, we must name the class of which they are a member:

2:43:10

練習9.7

size_type

練習9.8

1) string or value_type

2) readonly:const_reference ; write:reference



2:56:40

9.2.3. begin and end Members

9.2.3 begin 與 end 成員

3:5:00只有在常值物件上呼叫這些成員函式,我們才能取得一個常值的迭代器型別:

We get a const version of the iterators only when we call these functions on a const object.

就像對指標和參考,可以將非常值的轉型為常值,我們也可以將非常值的迭代器轉型成常值的:

As with pointers and references to const , we can convert a plain iterator to the corresponding const_iterator , but not vice versa.

但不能反轉

頁334

auto it7 = a.begin(); // const_iterator only if a is const

auto it8 = a.cbegin(); // it8 is const_iterator

所以要用const常值的迭代器,就只能用c版本的begin或end成員函式

The c versions let us get a const_iterator regardless of the type of the container.

養成好習慣:

Best Practices When write access is not needed, use cbegin and cend .

不想改動資料(這裡是迭代器指向的元素)時就用const版本的就對了

3:23:20

練習9.9

cbegin可以保證傳回的是常值的迭代器型別,而begin版本的只能在呼叫它的物件是常值時才會回傳常值的迭代器型別

練習9.10

3:24:59

3:44:10

vector<int> v1;

const vector<int> v2;

auto it1 = v1.begin(), it2 = v2.begin();

//it1 iterator,it2 const_iterator

//it3 const_iterator,it4 const_iterator

auto it3 = v1.cbegin(), it4 = v2.cbegin();

9.2.4. Defining and Initializing a Container

3:55:08

Initializing a Container as a Copy of Another Container

用另一個容器的拷貝來初始化一個容器。有兩種方式:

There are two ways to create a new container as a copy of another one: We can directly copy the container, or (excepting array ) we can copy a range of elements denoted by a pair of iterators.

可以直接對拷容器,也可以僅拷貝兩個迭代器所劃定的元素範圍內的所有元素(array容器例外)

用直接拷貝的方式初始化一個容器,其型別及其元素型別必須與要拷貝過來的那個容器相吻合

直接拷貝的方式和傳遞一對迭代器作為引數的方式拷貝,要求、條件是有不同的:

When we pass iterators, there is no requirement that the container types be identical. Moreover甚至, the element types in the new and original containers can differ as long as it is possible to convert (§ 4.11 , p. 159 ) the elements we’re copying to the element type of the container we are initializing:

4:23:30

頁335

// each container has three elements, initialized from the given initializers

list<string> authors = {"Milton", "Shakespeare", "Austen"};

vector<const char*> articles = {"a", "an", "the"};

list<string> list2(authors); // ok: types match

deque<string> authList(authors); // error: container types don't match//即使元素型別一樣

vector<string> words(articles); // error: element types must match//即使容器型別一致

// ok: converts const char* elements to string

forward_list<string> words(articles.begin(), articles.end());

可見只要是直接容器對拷,限制就很嚴格;若是傳遞一對迭代器作引數的方式拷貝(指定迭代器範圍的拷貝)就彈性許多,只要元素型別可以轉型就可以,連容器型別不同,都沒關係。

也可推知傳遞迭代器範圍的拷貝,實則應是對其元素的拷貝傳遞,而無關其容器型別,所以才無所限制,只要求元素型別之間具有轉型的可能即可:

The constructor that takes two iterators uses them to denote a range of elements that we want to copy. As

因此這兩種拷貝容器的初始化方式,可以姑命名為:對容器的拷貝和對元素的拷貝。

而直接拷貝容器,則是容器對容器,所以必須在容器型別與元素型別皆一致的情況下,才能用容器對拷。

4:32:40

Note: When we initialize a container as a copy of another container, the container type and element type of both containers must be identical.

5:37:00

Table 9.3. Defining and Initializing Containers

表9.3 :定義和初始化容器

C c ; 預設建構器。如果C是array,那麼c中的元素就是預設初始化的, 否則c為空。

C c1(c2)

C c1 = c2 c1是c2的一個拷貝。c1和c2必須有相同的型別(也就是說,它們必須有相同的容器型別,並存放相同的元素型別,對array來說還 必須有相同的大小)。

C c{a,b,c...} c是初始器串列中元素的一個拷貝。串列中的元素之型別必須與C的

C c={a,b,c...} 元素型別相容。對於array,串列必須有相同數目的元素,或者元素 數少於array的大小,缺少的任何元素都是值初始化的(§ 3.3.1 )。

C c(b, e) c是由b和e所標示的範圍中之元素的一個拷貝。元素的型別必須與 C的元素型別相容(對array無效)。

接受一個大小的建構器只對循序容器(不包括array)有效

C seq(n) seq有n個值初始化的元素,這個建構器是explicit ( § 7.5.4)的。 對string無效。

C seq(n,t) seq有帶有值t的n個元素。

C應該是小c,大小不會有元素,只是類別名。

5:31:30

頁336

List Initialization

沒有留言:

張貼留言