第三章 字串、向量和陣列
一、標頭檔案中不應該包含using namespace XXX;的宣告。
二、標準庫 string(標頭檔案為<string>)
string與”test“字串字面值不是同種型別的。
1、初始化
#include <string> using namespace std; string str1; // 預設為空串 string str2(str1); // str2為str1的副本,等價於 string str2 = str1; string str3("value"); // str3是字面值"value"的副本,除了字面值最後的哪個空字元,等價於 string str3 = "value"; string str4(n, 'c'); // 初始化為又n個字元c組成的串
2、基本操作(除基本的算術運算、邏輯運算、賦值操作等,主要介紹其函式)
string str; while(cin >> str) { if(!str.empty()) { cout << str[str.size() - 1] << endl; // 輸出str字串的最後一個字元 } } /* * 注意:1、string::size()函式返回的是string::size_type型別的值, * 可以在程式中使用C++11的auto或decltype對其進行型別推斷。 * 2、string::size_type是無符號型別的,如果一個表示式中有了 * string::size()那麼就不要再使用int等有符號型別了,以免 * 無符號和有符號混用帶來比較麻煩的問題。 */
// 把迴圈條件換成getline(cin, str),同樣輸入帶空格分隔的一句話,看看執行結果的變化
string str;
while(cin >> str)
{
cout << str << endl;
}
3、處理string中的字元(標準庫 cctype(標頭檔案<cctype>))
cctype標頭檔案中的主要函式:
- isalnum(c) // 當c是字母或數字時為真
- isalpha(c) // 當c是字母是為真
- iscntrl(c) // 當c是控制字元時為真
- isdigit(c) // 當c是數字時為真
- isgraph(c) // 當c不是空格但是可列印時為真
- islower(c) // 當c是小寫字母時為真
- isprint(c) // 當c是可列印字元時為真(即c是空格或c具有可視形式)
- ispunct(c) // 當c是標點符號時為真(即c不是控制符、數字、字母、可答應空白中的一種)
- isspace(c) // 當c是空白時為真(即c是空格、橫縱向製表符、回車符、換行符、進紙符中的一種)
- isupper(c) // 當c是大寫字母時為真
- isxdigit(c) // 當c是十六進位制數字時為真
- tolower(c) // 如果c是大寫字母,輸出對應的小寫字母,否則原樣輸出c
- toupper(c) // 如果c是小寫字母,輸出對應的大寫字母;否則原樣輸出c
處理string物件中的所有字元,我們除了使用傳統for迴圈外,也可以使用C++11中新增的範圍for。(*注意:範圍for語句體內不應該改變其所遍歷序列的大小)
// 把字串中的所有字元變為大寫
#include <iostream>
#include <string>
#include <cctype>
string str("Fuck you!");
for(auto &c : str)
{
c = toupper(c);
}
cout << str << endl;
處理string物件中的部分字元,可以使用下標運算子[],也可以使用迭代器。
三、標準庫 vector(標頭檔案<vector>)
1、初始化
vector<T> v1;
vector<T> v2(v1);
vector<T> v2 = v1;
vector<T> v3(n, val);
vector<T> v4(n);
vector<T> v5{a,b,c...};
vector<T> v5 = {a,b,c...};
2、相關操作
向向量末尾新增元素用string::vector.push_back(T)。也有empty()、size()等類似於string中的函式。對向量中元素的操作也和string中操作字元的方法類似。
四、迭代器(iterator):用來訪問標準庫容器物件的元素或string物件中的字元
1、所有容器以及string都有相應的迭代器型別,並且都有名為begin()和end()的成員函式。如果容器為空,那麼begin()和end()返回的是同一個迭代器,都是尾後迭代器。關於迭代器型別,string的迭代器型別表示為string::iterator iter、string::const_iterator citer,vector的迭代器型別表示為vector<T>::iterator iter、vector<T>::const_iterator citer。其實如果在C++11裡我們完全可以用auto型別說明符推斷一切型別。此外C++11中每個容器都有cbegin()和cend()函式用來返回常指標。
2、迭代器的運算子:
- *iter // 返回迭代器iter所指元素的引用
- iter->mem // 解引用iter並獲取該元素的命名為mem的成員,等價於(*iter).mem
- ++iter // 令iter指向容器中的下一個元素
- --iter // 令iter指向容器中的上一個元素
- ==,!=
*注意:但凡是使用了迭代器的額迴圈體,都不要向迭代器所屬的容器中新增元素,否則可能引起迭代器失效。
3、string和vector的迭代器提供了更多額外的運算子:
- iter + n
- iter - n
- iter += n
- iter -= n
- iter1 - iter2,返回型別為difference_type的帶符號整型數。
- >、>=、<、<=
// 迭代器實現二分搜尋
// text為有序序列
auto beg = text.begin(), end = text.end();
auto mid = text.begin() + (end - beg) / 2;
while(mid != end && *mid != sought)
{
if(sought < *mid)
end = mid;
else
beg = mid + 1;
mid = beg + (end - beg) / 2;
}
五、陣列(複合型別)
1、陣列在定義時緯度必須是已知的,也就是說,緯度必須是一個常量表達式(constexpr)。
顯式的初始化:
const unsigned sz = 3;
int a1[sz] = {0,1,2};
int a2[] = {0,1,2};
int a3[5] = {0,1,2}; // 初始化為{0,1,2,0,0}
// 字元陣列的特殊性
char ch1[] = {'c','+','+'};
char ch2[] = {'c','+','+','\0'}; // 含有顯式的空字元
char ch3[] = "c++"; // 陣列末尾含有隱式的空字元
char ch4[3] = "c++"; // 錯誤:沒有空間存放空字元
// 陣列不允許拷貝賦值,一些編譯器支援(編譯器擴充套件),但是儘量避免使用非標準特性
char a[] = "123";
char b[] = a; // 錯誤
a = b; // 錯誤
// 複雜的陣列宣告
int *pI[10]; // pI是含有10個整型指標的陣列
int (*pI)[10] = &arr; // pI指向一個緯度為10的整型陣列
int (&pI)[10] = arr; // pI為一個緯度為10的整型陣列的引用
2、訪問陣列的元素
使用陣列下標時,通常定義為size_t的無符號型別,在標準庫cstddef(標頭檔案<cstddef>)中定義。
3、指標和陣列
string str[] = {"123","abc","fuck"};
string *pStr1 = &str[1]; // pStr1是指向str第二個元素的指標
string *pStr2 = str; // pStr2是指向str第一個元素的指標
指標也是迭代器,獲取唯後指標的方法是,使用越界下標:
int i[] = {1,2,3};
int *e = &i[3]; // 下標3超出了陣列i的索引範圍
int *b = i;
for(b; b != e; b++) // 遍歷陣列i元素
{
cout << *b << endl;
}
為了更安全的使用指標,C++11中提供了兩個函式begin()、end()。
int *e = end(i);
int *b = begin(i);
指向陣列元素的指標可以做迭代器可以做的運算。e - p 的返回值是ptrdiff_t的一種帶符號的整型數,此型別也在標準庫cstddef中定義。
陣列指標的下標運算:
int ia[] = {0,1,2,3,4,5};
int *p = &ia[2];
int i = p[1]; // p[1]等價於*(p+1)
int j = p[-2]; // p[-2]等價於*(p-2)
// 以上操作必須保證訪問的陣列的索引是合法的
4、要儘量避免使用C風格的字串即字元陣列。有關C字串的處理函式在標準庫cstring(標頭檔案<cstring>)中。
5、string的c_str()成員函式可以返回C風格的字串。陣列可以初始化vector,如下:
int int_arr[] = {0,1,2,3,4,5};
vector<int> ivec(begin(int_arr), end(int_arr));
6、多維陣列:
初始化:
int ia[2][3] = {
{0, 1, 2},
{3, 4, 5},
{6, 7, 8}
};
*注意:要使用範圍for語句處理多維陣列,除了最內層迴圈外,其它所有的控制變數都應該是引用型別。
指標與多維陣列:
int ia[2][3];
int (*p)[3] = ia;
int p = &ia[1];
類型別名簡化多維陣列指標:
using int_array = int[3]; // 等價於下一語句
typedef int int_array[3];
for(int_array *p = ia; p != ia + 2; ++p)
for(int *q = *p; q != *p + 3; ++q)
cout << *q << ' ';
cout << endl;