1. 程式人生 > >第三章 字串、向量和陣列

第三章 字串、向量和陣列

一、標頭檔案中不應該包含using namespace XXX;的宣告。

二、標準庫 string(標頭檔案為<string>)

        string與”test“字串字面值不是同種型別的。

1、初始化

    #include <string>
    using namespace std;
    string str1;		// 預設為空串
    string str2(str1);		// str2為str1的副本,等價於 string str2 = str1;
    string str3("value");	// str3是字面值"value"的副本,除了字面值最後的哪個空字元,等價於 string str3 = "value";
    string str4(n, 'c');	// 初始化為又n個字元c組成的串

        2、基本操作(除基本的算術運算、邏輯運算、賦值操作等,主要介紹其函式)

    string str;
    
    while(cin >> str)
    {
        if(!str.empty())
        {
            cout << str[str.size() - 1] << endl;	// 輸出str字串的最後一個字元
        }
    }
    /*
     * 注意:1、string::size()函式返回的是string::size_type型別的值,
     *         可以在程式中使用C++11的auto或decltype對其進行型別推斷。
     *      2、string::size_type是無符號型別的,如果一個表示式中有了
     *         string::size()那麼就不要再使用int等有符號型別了,以免
     *         無符號和有符號混用帶來比較麻煩的問題。
     */
  
    // 把迴圈條件換成getline(cin, str),同樣輸入帶空格分隔的一句話,看看執行結果的變化
    string str;
    
    while(cin >> str)
    {
        cout << str << endl;
    }

        3、處理string中的字元(標準庫 cctype(標頭檔案<cctype>)

        cctype標頭檔案中的主要函式:

  • isalnum(c)    // 當c是字母或數字時為真
  • isalpha(c)     // 當c是字母是為真
  • iscntrl(c)       // 當c是控制字元時為真
  • isdigit(c)       // 當c是數字時為真
  • isgraph(c)    // 當c不是空格但是可列印時為真
  • islower(c)     // 當c是小寫字母時為真
  • isprint(c)      // 當c是可列印字元時為真(即c是空格或c具有可視形式)
  • ispunct(c)     // 當c是標點符號時為真(即c不是控制符、數字、字母、可答應空白中的一種)
  • isspace(c)    // 當c是空白時為真(即c是空格、橫縱向製表符、回車符、換行符、進紙符中的一種)
  • isupper(c)    // 當c是大寫字母時為真
  • isxdigit(c)     // 當c是十六進位制數字時為真
  • tolower(c)    // 如果c是大寫字母,輸出對應的小寫字母,否則原樣輸出c
  • toupper(c)    // 如果c是小寫字母,輸出對應的大寫字母;否則原樣輸出c

       處理string物件中的所有字元,我們除了使用傳統for迴圈外,也可以使用C++11中新增的範圍for。(*注意:範圍for語句體內不應該改變其所遍歷序列的大小

    // 把字串中的所有字元變為大寫
    #include <iostream>
    #include <string>
    #include <cctype>
    string str("Fuck you!");
    for(auto &c : str)
    {
        c = toupper(c);
    }
    cout << str << endl;

        處理string物件中的部分字元,可以使用下標運算子[],也可以使用迭代器。

三、標準庫 vector(標頭檔案<vector>)

        1、初始化

        vector<T> v1;

        vector<T> v2(v1);

        vector<T> v2 = v1;

        vector<T> v3(n, val);

        vector<T> v4(n);

        vector<T> v5{a,b,c...};

        vector<T> v5 = {a,b,c...};

        2、相關操作

        向向量末尾新增元素用string::vector.push_back(T)。也有empty()、size()等類似於string中的函式。對向量中元素的操作也和string中操作字元的方法類似。

四、迭代器(iterator):用來訪問標準庫容器物件的元素或string物件中的字元

        1、所有容器以及string都有相應的迭代器型別,並且都有名為begin()和end()的成員函式。如果容器為空,那麼begin()和end()返回的是同一個迭代器,都是尾後迭代器。關於迭代器型別,string的迭代器型別表示為string::iterator iter、string::const_iterator citer,vector的迭代器型別表示為vector<T>::iterator iter、vector<T>::const_iterator citer。其實如果在C++11裡我們完全可以用auto型別說明符推斷一切型別。此外C++11中每個容器都有cbegin()和cend()函式用來返回常指標。

        2、迭代器的運算子:

  • *iter                      // 返回迭代器iter所指元素的引用
  • iter->mem            // 解引用iter並獲取該元素的命名為mem的成員,等價於(*iter).mem
  • ++iter                   // 令iter指向容器中的下一個元素
  • --iter                     // 令iter指向容器中的上一個元素
  • ==,!=

        *注意:但凡是使用了迭代器的額迴圈體,都不要向迭代器所屬的容器中新增元素,否則可能引起迭代器失效。

3、string和vector的迭代器提供了更多額外的運算子:

  • iter + n
  • iter - n
  • iter += n
  • iter -= n
  • iter1 - iter2,返回型別為difference_type的帶符號整型數。
  • >、>=、<、<=
    // 迭代器實現二分搜尋
    // text為有序序列
    auto beg = text.begin(), end = text.end();
    auto mid = text.begin() + (end - beg) / 2;
    while(mid != end && *mid != sought)
    {
        if(sought < *mid)
            end = mid;
        else
            beg = mid + 1;
        mid = beg + (end - beg) / 2;
    }

五、陣列(複合型別)
        1、陣列在定義時緯度必須是已知的,也就是說,緯度必須是一個常量表達式(constexpr)。
        顯式的初始化:

    const unsigned sz = 3;
    int a1[sz] = {0,1,2};
    int a2[] = {0,1,2};
    int a3[5] = {0,1,2};		// 初始化為{0,1,2,0,0}
    // 字元陣列的特殊性
    char ch1[] = {'c','+','+'};
    char ch2[] = {'c','+','+','\0'};	// 含有顯式的空字元
    char ch3[] = "c++";			// 陣列末尾含有隱式的空字元
    char ch4[3] = "c++";		// 錯誤:沒有空間存放空字元
    // 陣列不允許拷貝賦值,一些編譯器支援(編譯器擴充套件),但是儘量避免使用非標準特性
    char a[] = "123";
    char b[] = a;			// 錯誤
    a = b; 				// 錯誤
    // 複雜的陣列宣告
    int *pI[10];			// pI是含有10個整型指標的陣列
    int (*pI)[10] = &arr;		// pI指向一個緯度為10的整型陣列
    int (&pI)[10] = arr;		// pI為一個緯度為10的整型陣列的引用

        2、訪問陣列的元素
        使用陣列下標時,通常定義為size_t的無符號型別,在標準庫cstddef(標頭檔案<cstddef>)中定義。

        3、指標和陣列

    string str[] = {"123","abc","fuck"};
    string *pStr1 = &str[1];	// pStr1是指向str第二個元素的指標
    string *pStr2 = str;	// pStr2是指向str第一個元素的指標

        指標也是迭代器,獲取唯後指標的方法是,使用越界下標:

    int i[] = {1,2,3};
    int *e = &i[3];	// 下標3超出了陣列i的索引範圍
    int *b = i;
    for(b; b != e; b++)	// 遍歷陣列i元素
    {
        cout << *b << endl;
    }

        為了更安全的使用指標,C++11中提供了兩個函式begin()、end()

    int *e = end(i);
    int *b = begin(i);

        指向陣列元素的指標可以做迭代器可以做的運算。e - p 的返回值是ptrdiff_t的一種帶符號的整型數,此型別也在標準庫cstddef中定義。
        陣列指標的下標運算:

    int ia[] = {0,1,2,3,4,5};
    int *p = &ia[2];
    int i = p[1];	// p[1]等價於*(p+1)
    int j = p[-2];	// p[-2]等價於*(p-2)
    // 以上操作必須保證訪問的陣列的索引是合法的

        4、要儘量避免使用C風格的字串即字元陣列。有關C字串的處理函式在標準庫cstring(標頭檔案<cstring>)中。
        5、string的c_str()成員函式可以返回C風格的字串。陣列可以初始化vector,如下:

    int int_arr[] = {0,1,2,3,4,5};
    vector<int> ivec(begin(int_arr), end(int_arr));

        6、多維陣列:

        初始化:

    int ia[2][3] = {
        {0, 1, 2},
        {3, 4, 5},
        {6, 7, 8}
    };

        *注意:要使用範圍for語句處理多維陣列,除了最內層迴圈外,其它所有的控制變數都應該是引用型別。

指標與多維陣列:

    int ia[2][3];
    int (*p)[3] = ia;
    int p = &ia[1];

        類型別名簡化多維陣列指標:

    using int_array = int[3];	// 等價於下一語句
    typedef int int_array[3];
    for(int_array *p = ia; p != ia + 2; ++p)
        for(int *q = *p; q != *p + 3; ++q)
            cout << *q << ' ';
    cout << endl;