【C++筆記】字串、向量和陣列
string(C++Primer 5th)
string & vector
string表示可變長的字元序列,vector存放的是某種給定型別物件的可變長序列。
using宣告
標頭檔案不應包含using宣告,否則標頭檔案的內容會拷貝到所有引用它的檔案中去。
string初始化賦值
使用等號=初始化的是拷貝初始化,不使用等號的稱為直接初始化。
size()函式的返回值是型別size_type,無符號整數型。如果n是負值的int,則s.size()
string s(10,'c'); // s的內容是cccccccccc
cin>>word ; // 遇到空格停止
getline(cin,line); // 讀入一整行,直到換行符
auto len = line.size(); // len的型別是string::size_type
string s1 = s + "," + "world"; // 加法需保證+兩次的運算物件至少有一個是string
string s2 = "hello" + "," + s1; // 錯誤:兩個運算物件都不是string
使用C++版本的C標準庫標頭檔案
一般來說,C++程式應該使用名為cname的標頭檔案而不使用name.h的形式。
基於範圍的for語句
// 統計string物件中標點符號的個數
string s("Hello World!!!");
decltype(s.size()) punct_cnt=0; // 用於統計個數,無符號整型確保下標不小於0
for(auto c:s)
if(ispunct(c)) punct_cnt++;
// 使用範圍for語句改變字串中的字元
string s("Hello World!!!");
for(auto &c:s)
c = toupper(c); // 將string物件轉換成大寫
cout << s << endl;
vector
vector概念
標準庫型別vector表示物件的集合,其中所有物件的型別都相同,通常也稱容器。
vector是模板而非型別,由vector生成的型別必須包含vector中元素的型別,如vector<int>、vector<vector<int>>
由於引用不是物件,所以不存在包含引用的vector。
列表初始化 OR 元素數量
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<string> v5{"hi"}; // v5有一個元素"hi"
vector<string> v6("hi"); // 錯誤,不能用字串字面值構建vector物件
vector<string> v7{10}; // v7有10個預設初始化的元素
vector<string> v8{10,"hi"}; // v8有10個值為"hi"的元素
向vector物件中新增元素
新增元素的方法是先建立一個空vector,然後在執行時利用vector的成員函式push_back向其中新增元素。PS:預先指定vector物件的容量沒有什麼必要,反而可能導致效能變差,不建議。
如果迴圈體內部含有向vector物件新增元素的語句,則不能使用範圍for迴圈!範圍for語句體內不應改變其所遍歷序列的大小。
vector<int> v; // 空vector物件
for(int i = 0; i != 100; i++)
v.push_back(i); // 往vector中新增0到99,不能用下標方式新增元素!
/*《C++Primer 5th》P94.練習3.16*/
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void main()
{
vector<int> v1, v2(10), v3(10, 42), v4{ 10 }, v5{ 10,42 };
vector<string> v6{ 10 }, v7{ 10,"hi" };
cout << "v1:" << endl; for (auto c : v1) cout << c << "\t";
cout << "\n\nv2:" << endl; for (auto c : v2) cout << c << "\t";
cout << "\n\nv3:" << endl; for (auto c : v3) cout << c << "\t";
cout << "\n\nv4:" << endl; for (auto c : v4) cout << c << "\t";
cout << "\n\nv5:" << endl; for (auto c : v5) cout << c << "\t";
cout << "\n\nv6:" << endl; for (auto c : v6) cout << c << "\t";
cout << "\n\nv7:" << endl; for (auto c : v7) cout << c << "\t";
getchar();
return ;
}
迭代器
相比下標運算子
所有標準庫容器如vector都可以使用迭代器,但是隻有少數幾種才同時支援下標運算子。嚴格來說,string物件不屬於容器型別,但它支援迭代器。
begin、end
begin成員負責返回指向第一個元素的迭代器,end成員負責返回尾元素的下一位置(該位置並無元素)的迭代器。end返回的迭代器通常稱為尾後迭代器或尾迭代器。
如果容器為空,則begin和end返回的是同一個迭代器即尾後迭代器。
所有標準庫容器的迭代器都定義了==和!=,但其中大多數並沒有定義<運算子!!!
for(auto it = s.begin(); it != s.end(); ++it)
*it = toupper(*it);
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
begin和end返回的具體型別由物件是否是常量來決定,如果物件是常量,begin和end返回const_iterator;如果物件不是常量,返回iterator。
cbegin、cend
C++11新標準引入了cbegin和cend,用法與begin、end相同,不同的是不論vector物件是否常量,返回值都是const_iterator。
解引用與成員訪問
it->mem和(*it).mem表達的意思相同。
(*it).empty() // 解引用it,然後呼叫empty成員
*it.empty() // 錯誤,試圖訪問it的empty成員(並不存在)
for(auto it = text.cbegin(); it != text.cend() && !it->empty(); ++it)
cout<< *it <<endl;
difference_type
迭代器的距離是指一個迭代器移動多少位置能追上另一個迭代器,型別位difference_type的帶符號整型數,可正可負。
二分搜尋
迭代器的經典演算法時二分搜尋。
陣列
陣列初始化
定義陣列時必須指定陣列型別,不允許使用auto關鍵字由初始值的列表推斷型別。和vector一樣,陣列元素應為物件,因此不存在引用的陣列。
// 錯誤示範
const char a[6] = "Daniel"; // 錯誤,沒有空間存放空字元
int a[] = {0,1,2};
int a2 = a; // 錯誤,不允許使用一個數組初始化另一個數組
a2 = a; // 錯誤,不能把一個數組直接賦值給另一個數組
陣列宣告
int *ptrs[10]; // ptrs是含有10個整型指標的陣列
int &refs[10] = /*?/; // 錯誤,不存在引用的陣列
int (*parray)[10] = &arr; // parray指向一個含有10個整數的陣列
int (&arrRef)[10] = arr; // arrRef引用一個含有10個整數的陣列
int *(&array)[10] = ptrs; // arry是陣列的引用,該陣列含有10個指標
指標也是迭代器
int arr = {0,1,2,3,4,5,6,7,8,9};
int *e = &arr[10]; // 指向arr尾元素的下一位置的指標
for(int *b = arr; b != e; ++b) // arr表示首元素的地址
cout << *b << endl;
說明:arr有10個元素,尾元素所在位置的索引是9,下一位置的不存在的元素用於提供地址初始化e。與尾後迭代器雷系,尾後指標不指向具體的元素,不能進行解引用或是遞增操作。
標準庫函式begin和end
C++11標準引入了begin和end兩個函式,與容器中的成員函式功能相似。但陣列不是類型別,所以begin和end不是陣列的成員函式,正確的使用形式是將陣列作為它們的引數。
int ia[] = {0,1,2,3,4,5,6,7,8,9};
int *beg = begin(ia); // 指向ia首元素的指標
int *last = end(ia); // 指向arr尾元素的下一位置的指標
指標運算
constexpr = size_t sz =5;
int arr[sz] = {1,2,3,4,5};
int *ip = arr;
int *ip1 = ip + 4; // ip1指向arr的尾元素arr[4]
auto n = end(arr)-begin(arr); // n為5,型別為ptrdiff_t帶符號型別
如果兩個指標分別指向不相關的物件,則不能比較它們。
如果p是空指標,允許給p加上或減去一個值為0的整型常量表達式。兩個空指標也允許彼此相減,結果為0。
C風格字串
cstring
定義在cstring標頭檔案中,cstring是C語言標頭檔案string.h的C++版本。
對大多數應用來說,使用標準庫string要比使用C風格字串更安全、更高效。
string s("hello world"); // string初始化
char *str = s; // 錯誤,不能用string物件初始化char*
const char *str = s.c_str();
// 正確,返回C風格的字串,不改變字元陣列內容,改變s的值會使返回的陣列失效
使用陣列初始化vector物件
int int_arr[] = {0,1,2,3,4,5};
vector<int> ivec(begin(int_arr),end(int_arr));
遍歷多維陣列
要使用範圍for語句處理多維陣列,除了最內層的迴圈外,其他所有迴圈的控制變數都應該是引用型別。
size_t cnt =0;
for(auto &row : ia) // for(auto row : ia )錯誤,自動轉換成ia首元素的指標
for(auto &col : row) // for(auto col : row)也正確
{
col = cnt;
++cnt;
}
for(auto p = begin(ia); p != end(ia); ++p)
for(auto q = begin(*p); q != end(*p); ++q)
cout << *q << ' ';