2019年9月30日 星期一
C++自修入門實境秀、C++ Primer 5版研讀秀 54/ ~ 9.2.4. Defining and Initializing a Co...
頁336
List Initialization
串列初始化
7:40
Sequential Container Size-Related Constructors
循序容器與大小相關的建構器
原文並無「與」!
和循序容器(sequential container)大小相關的建構器(constructor)
18:00
只帶一個容器大小引數的建構器,只對循序容器有效:
Note: The constructors that take a size are valid only for sequential containers; they are not supported for the associative containers.
對關聯式容器(associative container)無效
Library arrays Have Fixed Size
程式庫型別的array容器,有固定的大小
Just as the size of a built-in array is part of its type, the size of a library array is part of its type.
大小就是array型別的一部分,不管是是不是內建的型別
array<int, 42> // type is: array that holds 42 int s
array<string, 10> // type is: array that holds 10 string s
原來人這樣定義程式庫型別的array(library array)
To use an array type we must specify both the element type and the size:
要用array就一定要指定元素型別和元素多少(即容器大小)
array<int>::size_type j; // error: array<int> is not a type
只有元素型別是不足以構成一個array型別的
25:50
頁337
31:55
用串列初始化(list initialization)array其元素數量(即用來初始化元素的初始器數量)最多只能和定義的array一樣多:
If we list initialize the array , the number of the initializers must be equal to or less than the size of the array .
可見預設初始化和值初始化(value initialize)
的差別僅在有沒有提供初始器,若沒有,才是預設初始化;若有,則提供的不夠的,才會做值初始化(value initialize)
If there are fewer initializers than the size of the array , the initializers are used for the first elements and any remaining elements are value initialized (§ 3.3.1 , p. 98 ).
不管是要用預設初始化或值初始化,只要元素是類別型別(class type),那該類別必定要提供預設建構器(default constructor)來初始化其元素才行(才能進行值初始化):
In both cases, if the element type is a class type, the class must have a default constructor in order to permit value initialization:
array<int, 10> ia1; // ten default-initialized ints
array<int, 10> ia2 = {0,1,2,3,4,5,6,7,8,9}; // list initialization
array<int, 10> ia3 = {42}; // ia3[0] is 42, remaining elements are 0
這樣,預設初始化完全不提供初始器、串列初始化(list initialization)和值初始化有提供初始器,但不夠用(value initialize)就很清楚了
47:00
It is worth noting that although we cannot copy or assign objects of built-in array types (§ 3.5.1 , p. 114 ), there is no such restriction on array :
int digs[10] = {0,1,2,3,4,5,6,7,8,9};
int cpy[10] = digs; // error: no copy or assignment for built-in
array<int, 10> digits = {0,1,2,3,4,5,6,7,8,9};
array<int, 10> copy = digits; // ok: so long as array types match
可見內建型別的array不會出現array這個關鍵字
練習9.11
55:10
#include<vector>
using namespace std;
int main() {
vector<int> veci;
//Every container type defines a default constructor (§ 7.1.4, p. 263).
//With the exception of array, the default constructor creates an empty container of the specified type.
/*Again excepting array, the other constructors take arguments that
specify the size of the container and initial values for the elements.*/
vector<int>veci1(3,2);
vector<int>veci2(3);
/*There are two ways to create a new container as a copy of another one:
We can directly copy the container:*/
vector<int>veci3= veci1;//容器拷貝時,對拷雙方的容器型別與元素型別必須一致
vector<int>veci31(veci1);//the constructor that takes a container to copy
// or (excepting array) we can copy a range of elements denoted by a pair of iterators.:
vector<int>veci4(veci2.begin(), veci2.end());
vector<int>veci5 = {1,1,1};
vector<int>veci6(4,4);
veci6 = { 1,1,1 };//size會改變成串列的,而不是不足的用值初始化!veci6.size=3,veci6.capacity()=4;
//好像只有array因為是固定大小,串列初始化(list initialization)不夠的剩下才會用值初始化
}
1:24:09
練習9.12
一個是容器對拷,一個是元素對拷
因為迭代器指向的是元素,故是元素對拷
因為是元素對拷,所以只要元素型別有著轉型關係,就可以了。至於對拷雙方容器是什麼,是不相干的
練習9.13
1:42:30
#include<vector>
#include<list>
using namespace std;
int main() {
vector<int> veci{-1,-2,-3,0};
list<int> lsti{0,1,2,3};
vector<double> vecdi(veci.cbegin(),veci.cend());
vector<double> vecdl(lsti.cbegin(), lsti.cend());
}
因為容器型別與元素型別有所不同,所以要用元素拷貝的方式來初始化,也就是用帶有二個迭代器作為引數的建構器來初始化
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
訂閱:
文章 (Atom)