1. 程式人生 > >(C++Primer筆記)第3章

(C++Primer筆記)第3章

string表示可變長的字元序列,vector存放的是某種給定型別物件的可變長序列

3.1名稱空間的using宣告

作用域操作符::   

含義是編譯器應從操作符左側名字所表示的作用域中尋找右側那個名字。因此std::cin的意思就是要使用名稱空間std中的名字cin

有了using聲明後就無須專門的字首(形如名稱空間::)

▲每個名字都需要獨立的using宣告

▲標頭檔案不應包含using宣告

▲一點注意事項

3.2標準庫型別string

使用string型別必須包含string標頭檔案,string定義在std名稱空間中

3.2.1定義和初始化string物件

▲直接初始化和拷貝初始化

使用等號(=)初始化一個變數,實際執行的是拷貝初始化

不使用(=),執行的是直接初始化

3.2.2string物件傷的操作

getline is>>s

▲讀寫string物件

執行讀取操作(cin)時,string物件會自動忽略開頭的空白(即空白符 換行符 製表符等等)並從第一個真正的字元開始讀起,直到遇到下一處空白為止

▲讀取未知數量的string物件

while(cin>>word)

▲使用getline讀取一整行

getline函式的引數是一個輸入流和一個string物件,函式從給定的輸入流中讀入內容,直到遇到換行符

為止,然後把讀入的內容存入那個string物件中.

▲string的empty和size操作

▲string::size_type型別

一個無符號值,足夠存放下任何string物件的大小

▲比較string物件

▲為string物件賦值

▲兩個string物件相加

▲字面值和string物件相加

3.2.3處理string物件中的字元

在cctype標頭檔案中定義的一組標準庫函式

▲處理每個字元?使用基於範圍的for語句

範圍for語句

這種語句遍歷給定序列中的每個元素並對序列中的每個值執行某種操作。

其中,expression部分是一個物件,用於表示一個序列。declaration部分負責定義一個變數,該變數被用於訪問序列中的基礎元素。每次迭代,declaration部分的變數會被初始化為expression部分的下一個元素值。

▲使用範圍for語句改變字串中的字元

若想改變string物件中字元的值,必須把迴圈變數定義成引用型別

當引用作為迴圈控制變數時,這個變數實際上被依次繫結到了序列的每個元素上

▲只處理一部分字元

訪問string物件中的單個字元有兩種方式:

  1. 下標運算子[]
  2. 迭代器

▲使用下標執行迭代

▲使用下標執行隨機訪問

3.3標準庫型別vector

使用string型別必須包含string標頭檔案,string定義在std名稱空間中

vector是一個類模板,能使用成員函式

vector能容納絕大多數型別的物件作為其元素,但是因為引用不是物件,所以不存在包含引用的vector,除此之外,大都數(非引用)內建型別和類型別都可以構成vector物件,甚至組成vector的元素也可以是vector

vector<vector<string>> file;      //該向量的元素是vector物件

3.3.1 定義和初始化vector物件

·可以預設初始化vector物件,從而建立一個指定型別的空vector

最常見的就是先定義一個空vector,然後當執行時獲取到元素的值後再逐一新增

允許把一個vector物件的元素拷貝給另外一個vector物件,但這兩個vector物件的型別必須相同

▲列表初始化vector物件

C++提供了幾種不同的初始化方式。大多數情況下這些初始化方式可以相互等價地使用

,其中三種例外情況:

  1. 使用拷貝初始化(=),只能提供一個初始值
  2. 如果提供一個類內初始值,則只能使用拷貝初始化或使用花括號的形式初始化
  3. 如果提供的時初始值元素的列表,則只能把初始值都放在花括號裡進行列表初始化,而不能放在圓括號裡

▲建立指定數量的元素

▲值初始化

只提供vector物件容納的元素數量而不去略去初始值。這時,庫會建立一個值初始化的元素初值,並把它賦給容器中的所有元素

▲列表初始值還是元素數量?

小括號和花括號初始的區別

3.3.2向vector物件中新增元素

vector的成員函式push_back

最有效的方法是先定義一個空的vector物件,再在執行時向其新增具體值

▲向vector物件新增元素蘊含的程式設計假定

3.3.3其他vector物件的操作

只有當vector中元素的值可比較時,vector物件才可比較

▲計算vector內物件的索引

▲不能使用下標形式新增元素

vector物件(以及string物件)的下標運算子可用於訪問已存在的元素,而不能用於新增元素

3.4迭代器介紹

所有標準庫容器都可以使用迭代器,但是隻有少數幾種才同時支援下標運算子

string物件不屬於容器型別,但string支援很多與容器型別類似的操作

vector支援下標運算子,這點和string一樣

string支援迭代器,這點和vector物件也一樣

3.4.1使用迭代器

迭代器的成員函式begin和end

begin成員負責返回並指向第一個元素(或第一個字元)的迭代器

end成員負責返回指向容器(或string物件)“尾元素的下一位置”的迭代器

▲迭代器運算子

*iter iter->mem ++ -- != ==

▲將迭代器從一個元素移動到另外一個元素

end返回的迭代器並不實際指示某個元素,所以不能對其進性遞增或解引用的操作

▲迭代器型別

vector<int>::iterator it; //it能讀寫vector<int>的元素

string::iterator it2; //it2能讀寫string的字元

vector<int>::const_iterator it3; //it3能讀元素,但不能寫元素

string::const_iterator it4;//it4只能讀字元,不能寫字元

const­_iterator和常量指標差不多,能讀取但不能修改它所指的元素值相反,iterator的物件可讀可寫

若vector物件或string物件是一個常量,則只能使用const_iterator

若vector物件或string物件不是常量,則既能使用iterator也能使用const_iterator

▲begin和end運算子

begin和end返回的具體型別由物件是否是常量決定,如果物件是常量,begin和end返回const_iterator,如果不是,則返回iterator

cbegin和cend,無論vector物件是否是常量,返回的都是const­_iterator

▲給解引用和成員訪問操作

解引用迭代器可獲得迭代器所指的物件,如果該物件的型別恰好是類,就有可能希望進一步訪問它的成員

(*it).empty()         //解引用it,然後呼叫結果物件的empty成員

箭頭運算子 –>

it->mem 和 (*it).mem表達的意思相同

3.4.2迭代器運算

vector和string迭代器支援的運算

+ - += -= iter1-iter2 > >= < <=

▲迭代器的算術運算

兩個迭代器指向的是同一個容器中的元素或者尾元素的下一位置,就能將其相減,所得結果是兩個迭代器間的距離其型別為difference_type

▲和使用迭代器運算

二分搜尋法

3.5陣列

陣列是一種類似於標準庫型別vector的資料結構。

與vector相似的地方是,陣列也是存放型別相同的物件的容器

與vector不同的地方是,陣列的大小確定不變,不能隨意向陣列中增加元素

如果不清楚元素的確切個數,請使用vector

3.5.1定義和初始化內建陣列

陣列的維度必須是一個常量表達式

定義陣列的時候必須指定陣列的型別,不允許用auto關鍵字由初始值的列表推斷型別

和vector物件一樣,陣列的元素應為物件,因此不存在引用的陣列

▲顯示初始化陣列元素

可以對陣列的元素進行列表初始化,允許忽略陣列維度,編譯器會根據初始值的數量計算並推測出來

▲字元陣列的特殊性

可以用字串字面值對陣列初始化

注意字串字面值的結尾處還有一個空字元

▲不允許拷貝和賦值

不能將陣列的內容拷貝給其他陣列作為初始值,也不能用陣列為其他陣列賦值

▲理解複雜的陣列宣告

陣列本身就是物件,所以允許定義陣列的指標及陣列的引用

理解陣列宣告的含義,最好的方法是從陣列的名字開始由內向外的順序閱讀

3.5.2訪問陣列元素

當使用陣列下標時,通常將其定義為size­_t型別。size­_t是一種無符號型別,它被定義為足夠大以便能表示記憶體中任意物件的大小。

在cstddef標頭檔案中定義了size­_t型別

與vector和string一樣,當需要遍歷陣列的所有元素時,最好的辦法是使用範圍for語句

▲檢查下標的值

3.5.3指標和陣列

在大多數表示式中,使用陣列型別的物件其實是使用一個指向該陣列首元素的指標

一些情況下,陣列的操作其實是指標的操作

▲當使用auto變數的初始值時,推斷得到的型別是指標而不是陣列

其中一層意思是當使用陣列作為一個auto變數的初始值時,推斷得到的型別是指標而非陣列

當使用ia作為初始值時,編譯器實際執行的初始化過程類似於下面的形式:

當使用decltype關鍵字時,返回的型別是由10個整數構成的陣列

▲指標也是迭代器

可設法獲取尾元素之後那個並不存在的元素的地址

▲標準庫函式begin和end

陣列不是類型別,因此這個兩個函式不是成員函式。正確的使用形式是將陣列作為它們的引數

int ia = {0,1,2,3,4,5,6,7,8,9};  //ia是一個含有是10個整數的陣列

int *beg = begin(ia);   //指向ia首元素的指標

int *last = end(ia);   //指向ia尾元素的指標

▲指標運算

支援解引用、遞增、比較、與整數相加、兩個指標相減

只要兩個指標指向同一個陣列的元素,或者指向該陣列的尾元素的下一個位置,就能利用關係運算符進行比較。

兩個指標相減的結果的型別時一種名為ptrdiff_t的標準庫型別,和size_t一樣,也是一種定義在cstdeff標頭檔案中的及其相關的型別。因為差值可能為負,所以ptrdiff_t是一種帶符號型別。

如果兩個指標指向不相關的物件,則不能比較它們。

▲下標和指標

3.5.4 C風格字串

C風格字串:字串存放在字元陣列中以空字元結束,意味著最後一個字元後面跟著一個空字元(`\0`)

▲C標準庫string函式

strlen  strcmp  strcat  strcpy

傳入此類函式的指標必須指向以空字元作為結束的陣列

▲比較字串

比較標準庫string物件的時候,使用的是普通的關係運算符和相等性運算子

使用在C風格字串上,實際比較的將是指標而非字串本身:

當使用陣列的時候其實真正用的是指向陣列首元素的指標。因此上面的IF條件實際比較的是兩個const char*的值。這兩個指標指向的並非同一物件,所以得到未定義的結果。

相比較兩個C風格字串需要呼叫strcmp函式,此時比較的就不是指標了。如果兩個字串相等,strcmp返回0,如果前面的字串較大,返回正值;如果後面的字串較大,返回負值:

        

▲目標字串的大小由呼叫者決定

連線或拷貝C風格字串也與標準庫string物件的同類操作差別很大。

把string物件S1和S2連線起來:

這個操作放在陣列上會產生錯誤。表示式CA1+CA2,試圖將兩個指標相加,顯然這樣的曹祖沒什麼意義,而且肯定是非法的。

正確的方法是使用strcat函式和strcpy函式,。正確的方法是提供一個用於存放結果的陣列,該陣列必須足夠大以便容納下結果字串及末尾的空字元。

對大多數應用來說,使用標準庫string比使用C風格字串更安全、更有效

strcpy strcat

3.5.5與舊程式碼的介面

▲混用string物件和C風格字串

允許使用字串字面值來初始化string物件

更一般的情況是,任何出現字串字面值的地方都可以用以空字元結束的字元陣列來替代:

上述性質反過來就不成立了:如果程式的某處需要一個C風格字串,無法直接用string物件來代替它。

例如,不能用string物件直接初始化指向字元的指標。為了完成這個功能,string專門提供了一個名為c_str的成員函式

能用string物件直接初始化指向字元的指標

c_str函式的返回值是一個C風格的字串,即返回結果是一個指標,該指標指向以空字元結束的字元陣列。指標型別是const char*

▲使用陣列初始化vector物件

不允許使用一個數組為另一個內建型別的陣列賦值,也不允許使用vector物件初始化陣列。相反的,允許用陣列來初始化vector物件

只要指明拷貝區域的首元素地址和尾後地址就可以了

用於初始化vector物件的值也可能僅是陣列的一小部分