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 << ' ';