1. 程式人生 > 實用技巧 >《C++ Primer》筆記 第三章 字串、向量和陣列

《C++ Primer》筆記 第三章 字串、向量和陣列

  1. 位於標頭檔案的程式碼一般來說不應該使用using宣告。

  2. 如果使用等號(=)初始化一個變數,實際上執行的是拷貝初始化,編譯器把等號右側的初始值拷貝到新建立的物件中去。與之相反,如果不使用等號,則執行的是直接初始化。

  3. string物件會自動忽略開頭的空白(即空格符、換行符、製表符等)並從第一個真正的字元開始讀起,直到遇見下一處空白為止。

  4. string類的size函式返回的是一個string::size_type型別的值,它是一個無符號型別的值而且能足夠存放下任何string物件的大小。所有用於存放string類的size函式返回值的變數,都應該是string::size_type型別的。由於size函式返回的是一個無符號整型數,因此切記,如果在表示式中混用了帶符號數和無符號數將可能產生意想不到的結果。

  5. 如果一條表示式中已經有了size()函式就不要再使用int了,這樣可以避免混用int和unsigned可能帶來的問題。

  6. 當把string物件和字元字面值及字串字面值混在一條語句中使用時,必須確保每個加法運算子(+)的兩側的運算物件至少有一個是string。

  7. C++語言中的字串字面值並不是標準庫型別string的物件。切記,字串字面值與string是不同的型別。

  8. cctype標頭檔案中的函式

    函式 解釋
    isainum(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
  9. 範圍for語句遍歷給定序列中的每個元素並對序列中的每個值執行某種操作,其語法形式是:

      for (declaration : expression)
            statement
    
  10. 訪問string物件的下標運算子([])接受的輸入引數是string::size_type型別的值,這個引數表示要訪問的字元的位置;返回值是該位置上字元的引用。

  11. 初始化vector物件的方法

    方法 解釋
    vector v1 v1是一個空vctor,它潛在的元素是T型別的,執行預設初始化
    vector v2(v1) v2中包含有v1所有元素的副本
    vector v2 = v1 等價於v2(v1),v2中包含有v1所有元素的副本
    vector v3(n, val) v3包含了n個重複的元素,每個元素的值都是val
    vector v4(n) v4包含了n個重複地執行了值初始化的物件
    vector v5{a,b,c...} v5包含了初始值個數的元素,每個元素被賦予相應的初始值
    vector v5={a,b,c...} 等價於v5{a,b,c...}
  12. 初始化過程會盡可能地把花括號內的值當成是元素初始值的列表來處理,只有在無法執行列表初始化時才會考慮其他初始化方式。(例:vector<string> v1{10,"hi"};v1有10個值為"hi"的元素)確認無法執行列表初始化後,編譯器會嘗試用預設值初始化vector物件。

  13. vector<string> v2("hi");錯誤:不能使用字串字面值構建vector物件。因為()呼叫建構函式。

  14. 範圍for語句體內不應改變其所遍歷序列的大小。

  15. v1 = v2用v2中元素的拷貝替換v1中的元素,v1 = {a, b, c...}用列表中元素的拷貝替換v1中的元素

  16. 要使用size_type,需首先指定它是由哪種型別定義的。vector物件的型別總是包含著元素的型別:vector<int>::size_type正確,vector::size_type錯誤。

  17. 只要vector物件不是一個常量,就能向下標運算子返回的元素賦值。

  18. begin成員負責返回指向第一個元素的迭代器。end成員則負責返回指向容器“尾元素的下一位置”的迭代器。end成員返回的迭代器常被稱作尾後迭代器或者簡稱為為迭代器。如果容器為空,則begin和end返回的是同一個迭代器,都是尾後迭代器。

  19. 迭代器使用遞增(++)運算子來從一個元素移動到下一個元素。因為end返回的迭代器並不實際指示某個元素,所以不能對其進行遞增或解引用的操作。

  20. 使用iterator和const_iterator來表示迭代器的型別:

      vector<int>::iterator it; // it能讀寫vector<int>的元素
      string::iterator it2; // it2能讀寫string物件中的字元
      vector<int>::const_iterator it3; // it3只能讀元素,不能寫元素
      string::const_iterator it4; // it4只能讀字元,不能寫字元
    
  21. 我們認定某個型別是迭代器當且僅當它支援一套操作,這套操作使得我們能訪問容器的元素或者從某個元素移動到另外一個元素。

  22. 如果物件是常量,begin和end返回const_iterator;如果物件不是常量,返回iterator。為了便於專門得到const_iterator型別的返回值,C++11引入了兩個新函式,分別是cbegin和cend,不論vector物件(或string物件)本身是否是常量,返回值都是const_iterator。

  23. 解引用迭代器可獲得迭代器所指的物件,如果該物件的型別恰好是類,就有可能希望進一步訪問它的成員。

  24. 不能在範圍for迴圈中向vector物件新增元素。任何一種可能改變vector物件容量的操作,比如push_back,都會使該vector物件的迭代器失效。謹記,但凡是使用了迭代器的迴圈體,都不要向迭代器所屬的容器新增元素。

  25. 陣列的宣告形如a[d],其中a是陣列的名字,d是陣列的維度,維度必須大於0且必須是一個常量表達式。

  26. 和內建型別的變數一樣,如果在函式內部定義了某種內建型別的陣列,那麼預設初始化會令陣列含有未定義的值。

  27. 定義陣列的時候必須指定陣列的型別,不允許使用auto關鍵字由初始值的列表推斷型別。另外和vector一樣,陣列的元素應為物件,因此不存在引用的陣列。

  28. 不能將陣列的內容拷貝給其他陣列作為其初始值,也不能用陣列為其它陣列賦值。

  29. 理解複雜的陣列宣告:

      int *ptrs[10]; // ptrs是含有10個整形指標的陣列
      int &refs[10] = /* ? */; // 錯誤:不存在引用的陣列
      int (*Parray)[10] = &arr; // Parray指向一個含有10個整數的陣列
      int (&arrRef)[10] = arr; // arrRef引用一個含有10個整數的陣列
    
  30. 在使用陣列下標的時候,通常將其定義為size_t型別,size_t是一種機器相關的無符號型別,它被設計得足夠大以便能表示記憶體中任意物件的大小。在cstddef標頭檔案中定義了size_t型別。

  31. 在大多數表示式中,使用陣列型別的物件其實是使用一個指向該陣列首元素的指標。在一些情況下陣列的操作實際上是指標的操作,當使用陣列作為一個auto變數的初始值時,推斷得到的型別是指標而非陣列,如下例。而decltype(ia)返回的型別是由10個整數構成的陣列。

      int ia[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // ia是一個含有10個整數的陣列
      auto ia2(ia); // ia2是一個整型指標,指向ia的第一個元素
      ia2 = 42; // 錯誤:ia2是一個指標,不能用int值給指標賦值
    
  32. 指標也是迭代器。使用int *e = &arr[10];指向arr尾元素的下一位置的指標。也可以使用begin函式返回指向ia陣列首元素的指標,end函式返回指向ia陣列尾元素下一位置的指標。這兩個函式定義在iterator標頭檔案中。

      int ia[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // ia是一個含有10個整數的陣列
      int *beg = begin(ia); // 指向ia首元素的指標
      int *last = end(ia); // 指向arr尾元素的下一位置的指標
    
  33. 兩個指標相減的結果的型別是一種名為ptrdiff_t的標準庫型別。和size_t一樣,ptrdiff_t也是一種定義在cstddef標頭檔案中的機器相關的型別。因為差值可能為負值,所以ptrdiff_t是一種帶符號型別。

  34. 標準庫型別(比如vector和string)限定使用的下標必須是無符號型別,而內建的下標運算無此要求。內建的下標運算子所用的索引值不是無符號型別,這一點與vector和string不一樣。(陣列的下標運算實質上是指標加減操作,arr[1]等價於*(arr+1)

  35. 任何出現字串字面值的地方都可以用以空字元結束的字元陣列來替代:

    • 允許使用以空字元結束的字元陣列來初始化string物件或為string物件賦值。
    • 在string物件的加法運算中允許使用以空字元結束的字元陣列作為其中一個運算物件(不能兩個運算物件都是);在string物件的複合賦值運算中允許使用以空字元結束的字元陣列作為右側的運算物件。
  36. string物件的成員函式c_str()的返回值是一個C風格的字串。也就是說,函式的返回結果是一個指標,該指標指向一個以空字元結束的字元陣列。結果指標型別是const char*確保我們不會改變字元陣列的內容。如果後續的操作改變了string物件的值就可能讓之前返回的陣列失去效用。如果執行完c_str()函式後程序想一直都能使用其返回的陣列,最好將該陣列重新拷貝一份。

  37. 雖然不允許使用一個數組為另一個內建型別的陣列賦初值,也不允許使用vector物件初始化陣列,但是允許使用陣列來初始化vector物件。要實現這一目的,只需指明要拷貝區域的首元素地址和尾後地址就可以了。例:vector<int> ivec(begin(int_arr), end(int_arr));

  38. 現代的C++程式應當儘量使用vector和迭代器,避免使用內建陣列和指標;應該儘量使用string,避免使用C風格的基於陣列的字串。

  39. 使用範圍for語句處理多維陣列:

      int ia[3][4];
      size_t cnt = 0;
      for (auto &row : ia) // 對於外層陣列的每一個元素
            for (auto &col : row) // 對於內層陣列的每一個元素
            {
                  col = cnt; // 將下一個值賦給該元素
                  ++cnt; // 將cnt加1
            }
    
      for (const auto &row : ia) // 對於外層陣列的每一個元素
            for (auto col : row) // 對於內層陣列的每一個元素
                  cout << col << endl;
    
  40. 要使用範圍for語句處理多維陣列,除了最內層的迴圈外,其他所有迴圈的控制變數都應該是引用型別。

  41. 定義指向多維陣列的指標時,千萬別忘了這個多維陣列實際上是陣列的陣列。

  42. 使用標準庫函式begin和end簡化多維陣列的遍歷:

      for (auto p = begin(ia); p != end(ia); ++p)
      {
            for (auto q = begin(*p); q != end(*p); ++q)
                  cout << *q << ' ';
            cout << endl;
      }