字符串、向量和數組
1. 命名空間的using 聲明
-
簡單的聲明方式:
using namespace::name; //如using std::cin;
-
可放多行
-
頭文件中含有using後每個都會聲明瞭
1.1 string
位於#include裏的std中
-
定義和初始化string對象
=
:拷貝初始化- 不用
=
:直接初始化
string s1; //空的 string s2 = s1; //s1的副本 string s3 = "yes"; //同上 string s33("yes"); //同上 string s4(10, 'c'); //s4 內容是十個c
-
string 的操作
os<<s //將s寫入輸出流os當中,返回os is>>s //從is中讀取字符串給s。字符串以空格分格,返回is getline(is, s) //從is中讀取一行賦給s,返回is s.empty() //s爲空返回true否則false s.size() //長度 s[n] //返回s第n個字符的引用 s1+s2 //返回s1和s2連接的結果 s1=s2 s1==s2 s1!=s2 < <= > >= //利用字典序比較,對大小寫敏感
-
讀取未知對象
int main() { string s; while (cin >> s) //反覆讀取直到文件結束符 cout << s << endl; return 0; }
1.1.1 string::size_type 類型
size()
函數返回的是string::size_type
類型值- 無符號值
- 所以注意
(s.size() < n)
時如果n是負值 判斷結果幾乎是true;
1.1.2 比較
-
按字典序大小比較
-
前面相同則短的小
-
運算是從左到右且兩個之間必須有一個是string型
2. 處理string類型對象的字符
常用到的標準庫在頭文件
#include <cctype>
中
-
包含函數
isalnum(c) //當c是字母或數字爲真 isalpha(c) //當c是字母爲真 iscntrl(c) //當c是控制字符時爲真 isdigit(c) //當c時數字爲真 isgraph(c) //當c不是空格但可打印爲真 islower(c) //當c時小寫字母時爲真 upper isprint(c) //當c是可打印字符爲真 ispunct(c) //當c是標點符號爲真 isspace(c) //當c爲空白是真 stoll(c) //轉數字 tolower(c) //轉小寫 upper
c++11新語句for(range for) 遍歷每個元素並對值進行操作
-
for
for (declaration : expression) statement expression 部分是對象,表示一個序列 declaration 部分負責定義一個變量,該變量用於訪問序列的基礎元素,每次迭代變成expression部分的下一個元素 string str("some string"); for (auto c : str) //c初始爲str的元素 cout << c << endl; for (auto &c : str) //c引用了str的元素,所以可以更改值 c+=1; cout << str << endl;
-
調用下標時不要越界
3. vector
常被稱爲容器
#include <vector> std
中c++有類模板和函數模板
vector是一個類模板, 模板本身可以看作一份說明,編譯器根據模板創建類或者函數的過程稱實體化
vector<int> iv; //存放int
vector<vectot<string>> file; //向量元素是vector對象
-
引用不是對象,所以不包含在
vector
中 -
初始化
vector<T> v1; //空的 vector<T> v2(v1); //含有v1所有元素的副本 vector<T> v2 = v1; //同上 vector<T> v3(n, val); //包含m個重複元素,每個值都是val vector<T> v4(n); //包含n個重複性的初始化對象 vector<T> v5{a,b,c...}; //初始化爲各個值 vector<T> v5={a,b,c...}; //初始化爲各個值
-
元素的類型必須對應
-
初始化自定義元素列表只能是花括號
-
當花括號元素類型不對應時,會自動考慮默認值初始化
vector<string> ve{10} //因爲10int型考慮成,初始10個空元素 vector<string> ve{10,"123"} //因爲10int型考慮成,初始10個"123"元素
-
3.1 向vector添加元素
-
push_back()
: 負責把元素壓到尾端 -
注意範圍for語句不能改變遍歷序列的大小
-
vector
:其他常用操作v.empty() 判斷空 v.size() 取元素個數 v.push_back() 壓入元素 v[n] 返回第n個元素的引用 v1 = v2 拷貝 v1 = {a, b, c...} 拷貝 v1 != v2 v1 == v2 <, <=, >, >= 以字典序進行比較
-
同樣可以用
auto
-
size
返回值爲size_type
類型 必須vector<int>::size_type; //正確 vector::size_type; //錯誤
-
元素相同容量少的比容量多的小
-
大小關係與元素值定義的關係決定
3.1 vector 內元素的索引
與string 下標索引一樣
- 不能用下標形式添加元素
- 會導致所謂的緩衝區溢出
4. 迭代器
iterator
迭代器
- 所有標準庫都可以使用迭代器
- 提供對對象的間接訪問
- 有效迭代器
- 指向某個元素
- 容器中尾元素的下一個位置
- 其餘都時無效
4.1 使用迭代器
和指針不一樣的時,不是用取地址符
擁有迭代器的類型
- 成員
begin
和end
成員 第一個元素或者最後一個元素的下一個位置
4.1.1 運算符
-
標準容器迭代器的運算符
*iter 返回迭代器iter所指向的元素 iter->mem 解引用iter並獲取該元素的名爲mem的成員,(*iter).mem相同 ++iter 指向下一個元素 --iter 指向上一個元素 == != 判斷是否相同指向相同元素,並不是指元素值
-
和指針類似
- 可直接對值進行操作
4.1.2 迭代器類型
-
iterator
能讀寫vector<int>::iterator it; string::iterator it2;
-
const_iterator
只能讀和常量指針差不多
vector<int>::const_iterator it3;
4.1.3 begin和end
運算符
-
如果對象是常量返回const_iterator;
-
如果不是返回iterator
vector<int> v; const vector<int> v2; auto it1 = v.begin(); auto it2 = v2.begin(); const_iterator
c++11 爲const_iterator 引入cbegin 和cend
無論變量是什麼都返回const_iterator
-
調用類對象的成員函數
- 必須要加(*it).name(); 括號
- 加括號和新號纔是訪問元素本身
- 當然用
->
也可以直接使用
4.1.4 容易讓迭代失效的操作
- 不能操作任何改變對象容量的操作 如
push_back
4.2 迭代器運算
在string和vector的迭代器中提供了額外更多的運算符,可跨越多個元素,支持運算關係
-
所支持的關係表
操作 含義 iter+n 迭代器加上一個整數依然是迭代器相當於移動n個位置返回值 iter-n 同上類似 iter+=n 加上賦值語句 iter-=n 同上 iter1-iter2 得到距離的differece_type類型 > >= < <= -
difference_type
的帶符號的整型數 距離 兩個迭代器相減獲得的結果類型- string 和 vector都定義了
-
用迭代器寫的二分
auto beg = t.begin(), end = t.end(); //t的內容有序 auto mid = t.begin()+(end-beg)/2; while (mid != end && *mid != ans) { //查找ans元素 if (ans < *mid) end = mid; else beg = mid; mid = beg+(end-begin)/2; }
5. 數組
類似標準庫類型vector 的數據結構
-
不同點數組大小固定
-
定義和初始化
unsigned cn = 42; constexpr unsigned sz = 42; int arr[10]; //數組10個整數 int *parr[sz]; //含有42個整數指針 string dba[cnt]; //錯誤 不能用變量表達式 string str[get_size()]; //當get_size() 是constexpr時正確
- 數組定義時必須規定類型,不能用auto判斷
- 不存在引用數組
-
顯示初始化數組元素
列表初始化。如果忽略維度。
編譯器會根據初始值判斷,相反如果聲明知明維度,那初始值的總額不能超過維度大小。如果維度大於初始值則用靠前的元素,剩下的元素被初始化成默認值
const unsigned sz = 3;
int ial[sz] = {0, 1, 2};
int a2[] = {0, 1, 2};
int a3[5] = {0, 1, 2};
string a4[3] = {"hi", "bye"};
int a5[2] = {0,1,2}; // 錯誤
-
字符數組額外提供一個初始化方式
char a[] = "c++"; //自動添加結束符的空字符 char a4[3] = "c++"; //錯誤,沒空間存放空字符
-
數組不能直接拷貝和賦值給另一個數組
-
稍微複雜的數組聲明
int *pr[10]; //指針數組 int &re[10] = /* --*/; //錯誤,引用錯誤 int (*pra)[10] = &arr; //指向一個含有10個整數的數組 int (&arre)[10] = arr; //引用一個含有10個整數的數組
理解方法裏到外 從右到左 ,先讀括號內的,
所以第三個用例中,pra是指針-》它指向一個包含10個元素的int數組
引用相同
int *(&arr)[10] = pr; //arr是個引用,它引用了一個指向包含10個整數的指針int型
5.1 訪問數組元素
數組也能使用下標索引,通常定義爲
size_t
是一種無符號型的機器相關類型
在頭文件
cstddef
裏右定義size_t類型
unsigned sc[11] = {}; //11個分段,全被初始化爲0
for (auto i : sc) //對於sc
cout << i << " "; //輸出計數
cout << endl;
5.2 指針與數組
取地址符可適用與任何對象,數組有個獨特的特性直接用到數組的名字,編譯器會替換成數組第一個元素的指針
int arr[] = {/*..*/};
string *str = arr; //等價於str = &arr[0];
auto ia(arr); //同上
5.2.1 指針也是迭代器
指向數組的指針擁有更多功能
-
支持運算
int *p = arr; ++p; //指向下一個元素
-
c++11 引入
begin
和end
函數 可相當於迭代器使用了
auto *beg = begin(arr); //beg指向arr第一個元素
sizeof(a)/sizeof(a[0])
知識帶入可得到數組維度end(a)-begin(a) 也可以獲得
5.2.2 指針運算
解引用、遞增、比較、與整數相加減、指針相減。與迭代器一樣
-
auto *ip = arr; //指向a[0] auto *ip2 = ip+4; //指向 a[4]
-
不能指向超過數組的元素
- 這個錯誤編譯器發現不了
兩個指針相減得到的結果是printff_t得標準庫類型,和size_t 一樣定義在cstddef頭文件中
*
運算級比+,-高所以要加括號才能取運算後的解引用auto a = *(ip+4); //a = arr[4]; auto a = *ip+4; //a = arr[0]+4;
5.2.3 下標和指針
數組名字相當於用指針,所以同樣的指向數組中元素的指針,也可以用數組一樣的下標使用
auto *p = &ai[2]; //指向爲索引爲2的元素
auto j = p[1]; //p[1] 等價於*(p+1),就是ai[3]表示的元素
auto k = p[-2]; //就是ai[0];
c++也導入了c的字符串函數風格
#include <cstring>
strlen(p); 返回p的長度,空字符不計算在內
strcmp(p1,p2); 比較大小
strcat(p1, p2); 將p2附加到p1之後,返回p1
strcpy(p1, p2); 將p2拷貝給p1 返p1
使用的字符必須包括空字符
- 以上函數都要注意維度大小不能越界
- 允許用有空字符結束的字符數組來初始化string
- 運算時可以有一個是帶有空字符的字符數組
將string 轉char的成員函數
string s = "sadasd";
const char *str = s.c_str();
5.2.4 使用數組初始化vector
不允許vector初始化數組,但允許返過來
但需要首尾地址
int arr[] = {0, 1, 2, 3, 4, 5};
vector<int> ive(begin(arr), end(arr)); //所以也可以是一部分
vector<int> ive(arr+1, arr+4); //1-3 3個元素
6 多維數組
int a[3][4];
int a[3][4] = {
{0, 1, 2, 3} 第一行的初始值
{0, 1, 2, 3} 第2
{0, 1, 2, 3} 第3
};
int a[3][4] = {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}; //也可以這樣
int as[3][4] = {{0}, {1}, {2}}; //初始每行的首元素其他的按默認值初始化爲0
int as[3][4] = {0, 3, 4, 5}; //只初始第一行其他默認值爲0
int a[10][10][10]... = {0}; 所有元素初始爲0
- 運用:
ia[2][3] = arr[0][0][0]; //簡單的賦值
int (&row)[4] = ia[1]; //row引用綁定了ia的第二行數組,引用是數組類型的,並不是說他是個引用數組,引用不能是數組
用c++11 的for去賦值
size_t cnt = 0;
for (auto &row : as)
for (auto &col : row) {
col = cnt;
++cnt;
}
使用for除最內層外,其他都應該用引用類型
因爲數組的返回元素是指向首元素的指針得到的就是int *型所以無法在int *型中遍歷
6.1 類型別名化
using int_array = int[4]; //別名
typedef int int_array[4]; //同上
int ai[3][4];
for (int_array *p = ia; p != ia+3; ++p)
for (int *q = *p; q != *p+4; ++q)
cin << *q;
參考文獻:c++ prime 第五版