字符串、向量和數組

字符串、向量和數組


1. 命名空間的using 聲明

  • 簡單的聲明方式:

    using namespace::name; //如using std::cin;
    
    
  • 可放多行

  • 頭文件中含有using後每個都會聲明瞭

1.1 string

位於#include裏的std中

  • 定義和初始化string對象

    • =:拷貝初始化
    • 不用=:直接初始化
    string s1; //空的
    string s2 = s1; //s1的副本
    string s3 = "yes"; //同上
    string s33("yes"); //同上
    string s4(10, 'c'); //s4 內容是十個c
    
  • string 的操作

    os<<s //將s寫入輸出流os當中,返回os
    is>>s //從is中讀取字符串給s。字符串以空格分格,返回is
    getline(is, s) //從is中讀取一行賦給s,返回is
    s.empty() //s爲空返回true否則false
    s.size() //長度
    s[n] //返回s第n個字符的引用
    s1+s2 //返回s1和s2連接的結果
    s1=s2 
    s1==s2
    s1!=s2
    < <= > >= //利用字典序比較,對大小寫敏感
    
  • 讀取未知對象

    int main() {
        string s;
        while (cin >> s) //反覆讀取直到文件結束符
            cout << s << endl;
        return 0;
    }
    
1.1.1 string::size_type 類型
  • size()函數返回的是string::size_type類型值
    • 無符號值
    • 所以注意(s.size() < n)時如果n是負值 判斷結果幾乎是true;
1.1.2 比較
  • 按字典序大小比較

  • 前面相同則短的小

  • 運算是從左到右且兩個之間必須有一個是string型

2. 處理string類型對象的字符

常用到的標準庫在頭文件#include <cctype>

  • 包含函數

    isalnum(c) //當c是字母或數字爲真
    isalpha(c) //當c是字母爲真
    iscntrl(c) //當c是控制字符時爲真
    isdigit(c) //當c時數字爲真
    isgraph(c) //當c不是空格但可打印爲真
    islower(c) //當c時小寫字母時爲真
    	upper
    isprint(c) //當c是可打印字符爲真
    ispunct(c) //當c是標點符號爲真
    isspace(c) //當c爲空白是真
    stoll(c)   //轉數字
    tolower(c) //轉小寫
        upper
    

    c++11新語句for(range for) 遍歷每個元素並對值進行操作

  • for

    for (declaration : expression)
        statement
    expression 部分是對象,表示一個序列
    declaration 部分負責定義一個變量,該變量用於訪問序列的基礎元素,每次迭代變成expression部分的下一個元素
    string str("some string");
    for (auto c : str) //c初始爲str的元素
        cout << c << endl;
    for (auto &c : str) //c引用了str的元素,所以可以更改值
    	c+=1;
    cout << str << endl;
    
    
  • 調用下標時不要越界

3. vector

常被稱爲容器 #include <vector> std

c++有類模板和函數模板

vector是一個類模板, 模板本身可以看作一份說明,編譯器根據模板創建類或者函數的過程稱實體化

vector<int> iv; //存放int
vector<vectot<string>> file; //向量元素是vector對象
  • 引用不是對象,所以不包含在vector

  • 初始化

    vector<T> v1; //空的
    vector<T> v2(v1); //含有v1所有元素的副本 
    vector<T> v2 = v1; //同上
    vector<T> v3(n, val); //包含m個重複元素,每個值都是val
    vector<T> v4(n); //包含n個重複性的初始化對象
    vector<T> v5{a,b,c...}; //初始化爲各個值
    vector<T> v5={a,b,c...}; //初始化爲各個值
    
    • 元素的類型必須對應

    • 初始化自定義元素列表只能是花括號

    • 當花括號元素類型不對應時,會自動考慮默認值初始化

      vector<string> ve{10} //因爲10int型考慮成,初始10個空元素
      vector<string> ve{10"123"} //因爲10int型考慮成,初始10個"123"元素
      

3.1 向vector添加元素

  • push_back(): 負責把元素壓到尾端

  • 注意範圍for語句不能改變遍歷序列的大小

  • vector:其他常用操作

    v.empty() 判斷空
    v.size() 取元素個數
    v.push_back() 壓入元素
    v[n] 返回第n個元素的引用
    v1 = v2 拷貝
    v1 = {a, b, c...} 拷貝 
    v1 != v2
    v1 == v2
    <, <=>, >= 以字典序進行比較
    
  • 同樣可以用auto

  • size返回值爲size_type 類型 必須

    vector<int>::size_type; //正確
    vector::size_type; //錯誤
    
  • 元素相同容量少的比容量多的小

  • 大小關係與元素值定義的關係決定

3.1 vector 內元素的索引

與string 下標索引一樣

  • 不能用下標形式添加元素
    • 會導致所謂的緩衝區溢出

4. 迭代器

iterator 迭代器

  • 所有標準庫都可以使用迭代器
  • 提供對對象的間接訪問
  • 有效迭代器
    • 指向某個元素
    • 容器中尾元素的下一個位置
    • 其餘都時無效

4.1 使用迭代器

和指針不一樣的時,不是用取地址符

擁有迭代器的類型

  • 成員
    • beginend 成員 第一個元素或者最後一個元素的下一個位置
4.1.1 運算符
  • 標準容器迭代器的運算符

    *iter 返回迭代器iter所指向的元素
    iter->mem 解引用iter並獲取該元素的名爲mem的成員,(*iter).mem相同
    ++iter 指向下一個元素
    --iter 指向上一個元素
     ==  != 判斷是否相同指向相同元素,並不是指元素值
    
  • 和指針類似

    • 可直接對值進行操作
4.1.2 迭代器類型
  • iterator能讀寫

    vector<int>::iterator it;
    string::iterator it2;
    
  • const_iterator 只能讀

    和常量指針差不多

    vector<int>::const_iterator it3;
    
4.1.3 begin和end 運算符
  • 如果對象是常量返回const_iterator;

  • 如果不是返回iterator

    vector<int> v;
    const vector<int> v2;
    auto it1 = v.begin(); 
    auto it2 = v2.begin(); const_iterator
    

    c++11 爲const_iterator 引入cbegin 和cend

    無論變量是什麼都返回const_iterator

  • 調用類對象的成員函數

    • 必須要加(*it).name(); 括號
    • 加括號和新號纔是訪問元素本身
    • 當然用 ->也可以直接使用
4.1.4 容易讓迭代失效的操作
  • 不能操作任何改變對象容量的操作 如push_back

4.2 迭代器運算

在string和vector的迭代器中提供了額外更多的運算符,可跨越多個元素,支持運算關係

  • 所支持的關係表

    操作 含義
    iter+n 迭代器加上一個整數依然是迭代器相當於移動n個位置返回值
    iter-n 同上類似
    iter+=n 加上賦值語句
    iter-=n 同上
    iter1-iter2 得到距離的differece_type類型
    > >= < <=
  • difference_type 的帶符號的整型數 距離 兩個迭代器相減獲得的結果類型

    • string 和 vector都定義了
  • 用迭代器寫的二分

    auto beg = t.begin(), end = t.end(); //t的內容有序
    auto mid = t.begin()+(end-beg)/2;
    while (mid != end && *mid != ans) { //查找ans元素
    	if (ans < *mid)
            end = mid;
        else beg = mid;
        mid = beg+(end-begin)/2;
    }
    

5. 數組

類似標準庫類型vector 的數據結構

  • 不同點數組大小固定

  • 定義和初始化

    unsigned cn = 42;
    constexpr unsigned sz = 42;
    int arr[10]; //數組10個整數
    int *parr[sz]; //含有42個整數指針
    string dba[cnt]; //錯誤 不能用變量表達式
    string str[get_size()]; //當get_size() 是constexpr時正確
    
    • 數組定義時必須規定類型,不能用auto判斷
    • 不存在引用數組
  • 顯示初始化數組元素

    列表初始化。如果忽略維度。
    編譯器會根據初始值判斷,相反如果聲明知明維度,那初始值的總額不能超過維度大小。如果維度大於初始值則用靠前的元素,剩下的元素被初始化成默認值

const unsigned sz = 3;
int ial[sz] = {0, 1, 2};
int a2[] = {0, 1, 2};
int a3[5] = {0, 1, 2};
string a4[3] = {"hi", "bye"};
int a5[2] = {012}; // 錯誤
  • 字符數組額外提供一個初始化方式

    char a[] = "c++"; //自動添加結束符的空字符
    char a4[3] = "c++"; //錯誤,沒空間存放空字符
    
  • 數組不能直接拷貝和賦值給另一個數組

  • 稍微複雜的數組聲明

    int *pr[10]; //指針數組
    int &re[10] = /* --*/; //錯誤,引用錯誤
    int (*pra)[10] = &arr; //指向一個含有10個整數的數組
    int (&arre)[10] = arr; //引用一個含有10個整數的數組
    

    理解方法裏到外 從右到左 ,先讀括號內的,

    所以第三個用例中,pra是指針-》它指向一個包含10個元素的int數組

    引用相同

int *(&arr)[10] = pr; //arr是個引用,它引用了一個指向包含10個整數的指針int型

5.1 訪問數組元素

數組也能使用下標索引,通常定義爲size_t是一種無符號型的機器相關類型

在頭文件cstddef裏右定義size_t類型

unsigned sc[11] = {}; //11個分段,全被初始化爲0
for (auto i : sc)  //對於sc
    cout << i << " "; //輸出計數
cout << endl;

5.2 指針與數組

取地址符可適用與任何對象,數組有個獨特的特性直接用到數組的名字,編譯器會替換成數組第一個元素的指針

int arr[] = {/*..*/};
string *str = arr; //等價於str = &arr[0];
auto ia(arr); //同上

5.2.1 指針也是迭代器

指向數組的指針擁有更多功能

  • 支持運算

    int *p = arr;
    ++p; //指向下一個元素
    
  • c++11 引入beginend 函數 可相當於迭代器使用了

auto *beg = begin(arr); //beg指向arr第一個元素

sizeof(a)/sizeof(a[0])知識帶入可得到數組維度

end(a)-begin(a) 也可以獲得

5.2.2 指針運算

解引用、遞增、比較、與整數相加減、指針相減。與迭代器一樣

  • auto *ip = arr; //指向a[0]
    auto *ip2 = ip+4; //指向 a[4]
    
  • 不能指向超過數組的元素

    • 這個錯誤編譯器發現不了

    兩個指針相減得到的結果是printff_t得標準庫類型,和size_t 一樣定義在cstddef頭文件中

    *運算級比+,-高所以要加括號才能取運算後的解引用

    auto a = *(ip+4); //a = arr[4]; 
    auto a = *ip+4; //a = arr[0]+4; 
    
5.2.3 下標和指針

數組名字相當於用指針,所以同樣的指向數組中元素的指針,也可以用數組一樣的下標使用

auto *p = &ai[2]; //指向爲索引爲2的元素
auto j = p[1]; //p[1] 等價於*(p+1),就是ai[3]表示的元素
auto k = p[-2]; //就是ai[0];

c++也導入了c的字符串函數風格

#include <cstring>
strlen(p); 返回p的長度,空字符不計算在內
strcmp(p1,p2); 比較大小
strcat(p1, p2); 將p2附加到p1之後,返回p1
strcpy(p1, p2); 將p2拷貝給p1 返p1

使用的字符必須包括空字符

  • 以上函數都要注意維度大小不能越界
  • 允許用有空字符結束的字符數組來初始化string
  • 運算時可以有一個是帶有空字符的字符數組
將string 轉char的成員函數
    string s = "sadasd";
    const char *str = s.c_str();
5.2.4 使用數組初始化vector

不允許vector初始化數組,但允許返過來

但需要首尾地址

int arr[] = {0, 1, 2, 3, 4, 5};
vector<int> ive(begin(arr), end(arr)); //所以也可以是一部分
vector<int> ive(arr+1, arr+4); //1-3 3個元素

6 多維數組

int a[3][4];
int a[3][4] = {
    {0, 1, 2, 3} 第一行的初始值
    {0, 1, 2, 3}2
    {0, 1, 2, 3}3
};
int a[3][4] = {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}; //也可以這樣
int as[3][4] = {{0}, {1}, {2}}; //初始每行的首元素其他的按默認值初始化爲0
int as[3][4] = {0, 3, 4, 5}; //只初始第一行其他默認值爲0
int a[10][10][10]... = {0}; 所有元素初始爲0
  • 運用:
ia[2][3] = arr[0][0][0]; //簡單的賦值
int (&row)[4] = ia[1]; //row引用綁定了ia的第二行數組,引用是數組類型的,並不是說他是個引用數組,引用不能是數組

用c++11 的for去賦值

    size_t cnt = 0;
    for (auto &row : as)
        for (auto &col : row) {
            col = cnt;
            ++cnt;
        }

使用for除最內層外,其他都應該用引用類型

因爲數組的返回元素是指向首元素的指針得到的就是int *型所以無法在int *型中遍歷

6.1 類型別名化

using int_array = int[4]; //別名
typedef int int_array[4]; //同上
int ai[3][4];
for (int_array *p = ia; p != ia+3; ++p)
    for (int *q = *p; q != *p+4; ++q)
        cin << *q;

參考文獻:c++ prime 第五版

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