C++ 學習筆記之(3)-字符串、向量和數組
命名空間的using聲明
C++的命名空間的目的是爲了防止名字衝突。爲了使用命名空間的成員,可以使用using
聲明。有了using
聲明就無需專門的前綴,using
聲明的形式如下
using namespace::name;
- 每個名字都需要獨立的
using
聲明。 - 頭文件不應該包含
using
聲明,這是因爲頭文件的內容會拷貝到所有引用它的文件中去,如果頭文件裏有某個using
聲明,那麼每個使用了該頭文件的文件都會有此聲明。可能會導致名字衝突
注意:命名空間需要深入學習
標準庫類型 string
標準庫類型string
表示可變長的字符序列,string類型定義在命名空間std
中。
定義和初始化 string 對象
- 直接初始化:不使用等號的初始化
- 拷貝初始化:使用等號
=
初始化一個變量,編譯器把等號右側的初始值拷貝到新創建的對象中去。
注意:直接初始化和拷貝初始化需要深入學習
string 對象上的操作
類除了定義初始化其對象的方式外,還要定義對象所能執行的操作。
如果表達式中存在
size()
函數,就不要再使用int
類型了,因爲size
函數返回的是string
類的配套類型string::size_type
, 一個無符號類型的值,混用int
和unsigned
會出現問題。標準庫允許把字符字面值和字符串字面值轉換成
string
對象,即可以將其混用在一條語句中使用,但必須確保每個加法運算符兩側的運算對象至少有一個是string
string s1 = "hello", s2 = "world"; string s3 = s1 + ", " + s2 + '\n';
由於歷史原因,且爲了與
C
兼容,C++中的字符串字面值並不是標準庫類型string
的對象。切記,字符串字面值與string
是不同的類型。
處理 string 對象中的字符
我們經常需要單獨處理string
對象中的字符,cctype
頭文件定義了一組標準庫函數處理這部分工作
C語言頭文件形如
name.h
, C++將其命名爲cname
, C++程序應該使用後者,因爲標準庫的名字總能在命名空間std
中找到,若使用.h
頭文件,則無法找到。C++11使用範圍
for
語句可以對string
對象的每個字符操作,如果要改變string
隊形中該字符的值,必須把循環變量定義成引用類型。for(auto &c: str) c = toupper(c); // c 是一個引用,因此賦值語句將改變 s 中字符的值
標準庫類型 vector
標準庫類型vector
表示對象的集合,其中所有對象的類型都相同,也被稱爲容器
vector
是模板而非類型,由vector
生成的類型必須包含vector
中元素的類型,例如vector<int>
vector
能容納絕大多謝類型對象作爲元素,但不能是引用,因爲引用不是對象,元素也可以是vector
定義和初始化 vector 對象
下表列出了定義vector
對象的常用方法
列表初始化:C++11新標準提供的爲
vector
對象的元素賦初值的方法。拷貝初始化:即使用
=
時,只能提供一個初始值如果提供的是一個類內初始值,則只能用拷貝初始化或使用花括號的形式初始化
如果提供是的初始元素值的列表,則只能把初始值放在花括號裏進行列表初始化,不能放在圓括號。
vector<int> v1(10); // v1 有 10 個元素,每個值都爲0 vector<int> v2{10}; // v2 有 1 個元素,該元素的值是10 vector<int> v3(10, 1); // v3 有 10 個元素,每個的值都是1 vector<int> v4{10, 1}; // v4 有 2 個元素,值爲 10 和 1 // 若花括號提供的值不能用來列表初始化,則考慮用值構造 vector 對象 vector<string> v5{"hi"}; // 列表初始化,v5 有一個元素 vector<string> v6("hi"); // 錯誤:不能使用字符串字面值構建 vector 對象 vector<string> v7{10}; // v7 有 10 個默認初始化的元素 vector<string> v8{10, "hi"}; // v8 有 10 個值爲 "hi" 的元素
上面後四條語句除了第二條語句都用了花括號,但只有v5是列表初始化。要想列表初始化,花括號裏的值必須與元素類型相同。
其他 vector 操作
vector
對象以及string
對象的下標運算符可用於訪問已存在的元素,而不能用於添加元素- 範圍
for
語句體內不應改變其所遍歷序列的大小
迭代器介紹
迭代器提供了對對象的間接訪問,對象是容器中的元素或者string
對象中的字符,迭代器有有效和無效之分,有效的迭代器指向某個元素,或容器中尾元素的下一位置,其他情況都是無效。
使用迭代器
- 有迭代器的類型都擁有返回迭代器的成員, 比如
end
成員,用來返回指向容器尾元素的下一位置
的迭代器, 也被稱爲尾後迭代器
。如果容器爲空,則begin
和end
返回的是同一個迭代器,都是尾後迭代器
- 儘量使用!=
而非<
, 因爲所有標準庫容器的迭代器都定義了==
和!=
, 但大多都沒有定義<
運算符
標準庫類型一般使用
iterator
和const_iterator
來表示迭代器類型vector<int>::iterator it; // it 能讀寫 vector<int> 的元素 vector<int>::const_iterator it3; // it3 只能讀元素,不能寫元素
如果
vector
或string
對象是常量,則只能使用const_iterator
凡是使用了迭代器的循環體,都不要向迭代器所屬的容器添加元素
迭代器運算
- 兩個迭代器相減的結果類型是名爲
difference_type
的帶符號整型數, 表示兩個迭代器的距離
數組
數組與vector
類似,但大小確定不變。
定義和初始化內置數組
數組的維度在編譯時應該是已知的, 即維度必須是一個常量表達式
unsigned cnt = 42; // 不是常量表達式 constexpr unsigned sz = 42; // 常量表達式 int arr[10]; // 含有 10 個整數的數組 int *parr[sz]; // 含有 42 個整型指針的數組 string bad[cnt]; // 錯誤: cnt 不是常量表達式 string strs[get_size()]; // 當 get_size 是 constexpr 時正確,否則錯誤
默認情況下,數組的元素被默認初始化,如果在函數體內部定義的話,則含有未定義的值
定義數組的時候必須指定數組的類型,不允許使用
auto
關鍵字推斷類型int *ptrs[10]; // ptrs 是含有 10 個整型指針的數組 int &refs[10] = /* ? */; // 錯誤:不存在引用的數組 int (*parray)[10] = &arr; // Parray 指向一個含有 10 個整數的數組 int (&arrRef)[10] = arr; // arrRef 引用一個含有 10 個整數的數組
使用數組下標訪問數組元素的時候,類型爲
size_t
, 一種與機器相關的無符號類型,在cstdef
頭文件中。
指針和數組
使用數組時,編譯器一般會把它轉換成指針
在大多數表達式中,使用數組類型的對象其實是使用一個指向該數組首元素的指針
string nums[] = {"one", "two", "three"}; // 數組的元素是 string 對象 string *p = &nums[0]; // p 指向 nums 的第一個元素 string *p2 = nums; // 同上 auto nums2(nums); // nums2 是一個 string 類型指針,指向 nums 的第一個元素,等價於 auto nums2(&nums[0]) decltype(nums) nums3 = {"three", "two", "one"}; // nums3 是一個含有三個string類型對象的數組
C++11新標準引入了兩個名爲
begin
和end
的函數,與容器的同名函數功能類似,begin
函數返回指向數組首元素指針,end
函數返回指向數組尾元素下一位置的指針int ia[] = {0, 1, 2, 3, 4, 5}; int *beg = begin(ia); // 指向 ia 首元素的指針 int *last = end(ia); // 指向 arr 尾元素的下一位置的指針
兩個指針興建的結果是他們之間的距離,結果是
ptrdiff_t
的帶符號標準庫類型,定義在cstddef
中標準庫類型限定使用的下標必須是無符號類型,而內置的下標運算無此要求,即內置的下表運算符可以是負值。
C 風格字符串
字符串字面值是一種通用結構的實例,該結構即是繼承於C的C風格字符串,習慣是字符串存放在字符數組中,並以空字符結束\0
上述函數不負責驗證其字符串參數(比如,字符串未以空字符結尾,會報錯)
爲了銜接使用了C藉口的C++程序,C++提供一組功能;
允許C風格字符串初始化
string
對象,反之不行,但可以使用std::c_str
函數獲取C風格字符串,返回結果指針,類型是const char *
允許用數組初始化
vector
對象
int int_arr[] = {0, 1, 2, 3, 4, 5}; vector<int> ivec(begin(int_arr), end(int_arr));
多維數組
嚴格來說,C++沒有多維數組,通常訴說的多維數組其實就是數組的數組
int ia[3][4] = {{0}, {4}, {8}}; // 顯示的初始化每行的首元素
int ix[3][4] = {0, 3, 6, 9}; // 顯示的初始化第一行,其他元素執行值初始化
要使用範圍
for
語句處理多維數組時,除了最內層的循環外,其他所有循環的控制變量都應該是引用類型。for (auto row : ia) for(auto col : row)
如上所示,由於
row
不是引用類型,所以編譯器初始化row
時會自動將這些數組形式的元素轉換成指向該數組內榮元素的指針,這樣row
的類型是int *
, 所以內層循環會不合法。
結語
string
和vector
是兩種最重要的標準庫類型。string
對象是一個可變長的字符序列,vector
對象是一組同類型對象的容器
迭代器允許對容器中的對昂進行間接訪問,對於string
對象和vector
對象來說,可以通過迭代器訪問元素或者在元素箭移動
數組和指向數組元素的指針在一個較低層次上實現了與標準庫類型string
和vector
類似的功能。一般啦說,應該優先選用標準庫提供的類型,之後在考慮C++語言內置的底層的替代品數組或指針。