1. 程式人生 > >【C++筆記】字串、向量和陣列

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