C++ 學習筆記之(3)-字符串、向量和數組

C++ 學習筆記之(3)-字符串、向量和數組

命名空間的using聲明

C++的命名空間的目的是爲了防止名字衝突。爲了使用命名空間的成員,可以使用using聲明。有了using聲明就無需專門的前綴,using聲明的形式如下

using namespace::name;
  • 每個名字都需要獨立的using聲明。
  • 頭文件不應該包含using聲明,這是因爲頭文件的內容會拷貝到所有引用它的文件中去,如果頭文件裏有某個using聲明,那麼每個使用了該頭文件的文件都會有此聲明。可能會導致名字衝突

注意:命名空間需要深入學習

標準庫類型 string

標準庫類型string表示可變長的字符序列,string類型定義在命名空間std中。

定義和初始化 string 對象

  • 直接初始化:不使用等號的初始化
  • 拷貝初始化:使用等號=初始化一個變量,編譯器把等號右側的初始值拷貝到新創建的對象中去。

注意:直接初始化和拷貝初始化需要深入學習

string_initialize_methods

string 對象上的操作

類除了定義初始化其對象的方式外,還要定義對象所能執行的操作。
string_operation_methods

  • 如果表達式中存在size()函數,就不要再使用int類型了,因爲size函數返回的是string類的配套類型string::size_type, 一個無符號類型的值,混用intunsigned會出現問題。

  • 標準庫允許把字符字面值和字符串字面值轉換成string對象,即可以將其混用在一條語句中使用,但必須確保每個加法運算符兩側的運算對象至少有一個是string

    string s1 = "hello", s2 = "world";
    string s3 = s1 + ", " + s2 + '\n';
  • 由於歷史原因,且爲了與C兼容,C++中的字符串字面值並不是標準庫類型string的對象。切記,字符串字面值與string是不同的類型。

處理 string 對象中的字符

我們經常需要單獨處理string對象中的字符,cctype頭文件定義了一組標準庫函數處理這部分工作

cctype_functions

  • C語言頭文件形如name.h, C++將其命名爲cname, C++程序應該使用後者,因爲標準庫的名字總能在命名空間std中找到,若使用.h頭文件,則無法找到。

  • C++11使用範圍for語句可以對string對象的每個字符操作,如果要改變string隊形中該字符的值,必須把循環變量定義成引用類型。

    for(auto &c: str)
    c = toupper(c);  // c 是一個引用,因此賦值語句將改變 s 中字符的值

標準庫類型 vector

標準庫類型vector表示對象的集合,其中所有對象的類型都相同,也被稱爲容器

  • vector是模板而非類型,由vector生成的類型必須包含vector中元素的類型,例如vector<int>
  • vector能容納絕大多謝類型對象作爲元素,但不能是引用,因爲引用不是對象,元素也可以是vector

定義和初始化 vector 對象

下表列出了定義vector對象的常用方法

vector_initialize

  • 列表初始化:C++11新標準提供的爲vector對象的元素賦初值的方法。

  • 拷貝初始化:即使用=時,只能提供一個初始值

  • 如果提供的是一個類內初始值,則只能用拷貝初始化或使用花括號的形式初始化

  • 如果提供是的初始元素值的列表,則只能把初始值放在花括號裏進行列表初始化,不能放在圓括號。

    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 對象
    vector<string> v5{"hi"};  // 列表初始化,v5 有一個元素
    vector<string> v6("hi");  // 錯誤:不能使用字符串字面值構建 vector 對象
    vector<string> v7{10};  // v7 有 10 個默認初始化的元素
    vector<string> v8{10, "hi"};  // v8 有 10 個值爲 "hi" 的元素
  • 上面後四條語句除了第二條語句都用了花括號,但只有v5是列表初始化。要想列表初始化,花括號裏的值必須與元素類型相同。

其他 vector 操作

vector_operations_methods

  • vector對象以及string對象的下標運算符可用於訪問已存在的元素,而不能用於添加元素
  • 範圍for語句體內不應改變其所遍歷序列的大小

迭代器介紹

迭代器提供了對對象的間接訪問,對象是容器中的元素或者string對象中的字符,迭代器有有效和無效之分,有效的迭代器指向某個元素,或容器中尾元素的下一位置,其他情況都是無效。

使用迭代器

  • 有迭代器的類型都擁有返回迭代器的成員, 比如end成員,用來返回指向容器尾元素的下一位置的迭代器, 也被稱爲尾後迭代器。如果容器爲空,則beginend返回的是同一個迭代器,都是尾後迭代器

iterator_operator
- 儘量使用!=而非<, 因爲所有標準庫容器的迭代器都定義了==!=, 但大多都沒有定義<運算符

  • 標準庫類型一般使用iteratorconst_iterator來表示迭代器類型

    vector<int>::iterator it;  // it 能讀寫 vector<int> 的元素
    vector<int>::const_iterator it3;  // it3 只能讀元素,不能寫元素
  • 如果vectorstring對象是常量,則只能使用const_iterator

  • 凡是使用了迭代器的循環體,都不要向迭代器所屬的容器添加元素

迭代器運算

iterator_arithmetic

  • 兩個迭代器相減的結果類型是名爲difference_type的帶符號整型數, 表示兩個迭代器的距離

數組

數組與vector類似,但大小確定不變。

定義和初始化內置數組

  • 數組的維度在編譯時應該是已知的, 即維度必須是一個常量表達式

    unsigned cnt = 42;  // 不是常量表達式
    constexpr unsigned sz = 42;  // 常量表達式
    int arr[10];  // 含有 10 個整數的數組
    int *parr[sz];  // 含有 42 個整型指針的數組
    string bad[cnt];  // 錯誤: cnt 不是常量表達式
    string strs[get_size()];  // 當 get_size 是 constexpr 時正確,否則錯誤
  • 默認情況下,數組的元素被默認初始化,如果在函數體內部定義的話,則含有未定義的值

  • 定義數組的時候必須指定數組的類型,不允許使用auto關鍵字推斷類型

    int *ptrs[10];  // ptrs 是含有 10 個整型指針的數組
    int &refs[10] = /* ? */;  // 錯誤:不存在引用的數組
    int (*parray)[10] = &arr;  // Parray 指向一個含有 10 個整數的數組
    int (&arrRef)[10] = arr;  // arrRef 引用一個含有 10 個整數的數組
  • 使用數組下標訪問數組元素的時候,類型爲size_t, 一種與機器相關的無符號類型,在cstdef頭文件中。

 指針和數組

  • 使用數組時,編譯器一般會把它轉換成指針

  • 在大多數表達式中,使用數組類型的對象其實是使用一個指向該數組首元素的指針

    string nums[] = {"one", "two", "three"};  // 數組的元素是 string 對象
    string *p = &nums[0];  // p 指向 nums 的第一個元素
    string *p2 = nums;  // 同上
    
    auto nums2(nums);  // nums2 是一個 string 類型指針,指向 nums 的第一個元素,等價於 auto nums2(&nums[0])
    
    decltype(nums) nums3 = {"three", "two", "one"}; // nums3 是一個含有三個string類型對象的數組
  • C++11新標準引入了兩個名爲beginend的函數,與容器的同名函數功能類似,begin函數返回指向數組首元素指針,end函數返回指向數組尾元素下一位置的指針

    int ia[] = {0, 1, 2, 3, 4, 5};
    int *beg = begin(ia);  // 指向 ia 首元素的指針
    int *last = end(ia);  // 指向 arr 尾元素的下一位置的指針
  • 兩個指針興建的結果是他們之間的距離,結果是ptrdiff_t的帶符號標準庫類型,定義在cstddef

  • 標準庫類型限定使用的下標必須是無符號類型,而內置的下標運算無此要求,即內置的下表運算符可以是負值。

C 風格字符串

字符串字面值是一種通用結構的實例,該結構即是繼承於C的C風格字符串,習慣是字符串存放在字符數組中,並以空字符結束\0
C_style_string_methods

  • 上述函數不負責驗證其字符串參數(比如,字符串未以空字符結尾,會報錯)

  • 爲了銜接使用了C藉口的C++程序,C++提供一組功能;

    • 允許C風格字符串初始化string對象,反之不行,但可以使用std::c_str函數獲取C風格字符串,返回結果指針,類型是const char *

    • 允許用數組初始化vector對象

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

多維數組

嚴格來說,C++沒有多維數組,通常訴說的多維數組其實就是數組的數組

int ia[3][4] = {{0}, {4}, {8}};  // 顯示的初始化每行的首元素
int ix[3][4] = {0, 3, 6, 9};  // 顯示的初始化第一行,其他元素執行值初始化
  • 要使用範圍for語句處理多維數組時,除了最內層的循環外,其他所有循環的控制變量都應該是引用類型。

    for (auto row : ia)
    for(auto col : row)

    如上所示,由於row不是引用類型,所以編譯器初始化row時會自動將這些數組形式的元素轉換成指向該數組內榮元素的指針,這樣row的類型是int *, 所以內層循環會不合法。

結語

stringvector是兩種最重要的標準庫類型。string對象是一個可變長的字符序列,vector對象是一組同類型對象的容器

迭代器允許對容器中的對昂進行間接訪問,對於string對象和vector對象來說,可以通過迭代器訪問元素或者在元素箭移動

數組和指向數組元素的指針在一個較低層次上實現了與標準庫類型stringvector類似的功能。一般啦說,應該優先選用標準庫提供的類型,之後在考慮C++語言內置的底層的替代品數組或指針。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章