重拾C++ 順序容器

C++標準庫string
與其它常見的初始化方式不同的是,其提供可字符重複(及次數)的初始化方法。其是否使用"="進行初始化與構造函數
的設定有關。(注意初始化時不能使字符串重複初始化而來)
類的初始化方式兼有聲明的作用,即同時聲明並初始化類實例,這對一些腳本語言(python:基本沒有聲明的意義,運行時解析)
是無用的。


string具有字符粘貼能力(python 同樣有),這爲多行輸入提供了方便。
string(有size empty方法)與一般腳本str的不同在於,其提供了getline函數,可以輸入流(cin或一些文件輸入流)作爲第一個參數,第二個是輸入對象,
這樣讀入輸入流的第一行。這個函數可以用於循環輸入(搭配while循環)。
關於string提供的"+"運算符,值得注意的是不能直接對兩個字符串常量直接使用,其非string類型。


C++中cctype.h中定義了若干對字符進行判斷處理的函數。(include時去掉.h)
由於標準庫string提供的方法僅此而已,所以對字符串中的字符進行處理多依賴於cctype庫,遍歷的操作多依賴於範圍for循環。
(進行改變時使用引用:&,這可以對照於python的處理)
還有另外一種處理方式,利用迭代器及algorithm for_each函數但這裏需要注意的是,要對其進行修改(toupper)需要將參數設定爲引用。
(這也是合理的)這裏雖然沒有明確說明,但是sting vector等都具有begin end等迭代器。


迭代器與指針類似,(* -> ++ == !=)在對迭代器進行增減循環(一般會利用for_each進行替代)時判斷條件多要採用"!=",而非沒有規定序
的"<"。


迭代器類型:對於每一個容器,相對應的有迭代器,(Ex : vector::iterator)相應的const迭代器(vector::const_iterator)
與const指針相似,不能對指向的元素進行修改。(相應地對應於cbegin、cend)
這裏操作指向的成員多使用->,否則在使用*要加"()"規定優先級(否則出錯)
任何想要改變vector大小的操作都會使迭代器失效。
迭代器的基本運算與指針相似,特別地都被重載了 "+="等運算符。


剩餘的部分涉及與舊的C的接口,不推薦使用(strlen, strcmp ...)






順序容器:
順序容器主要包括(vector deque list forward_list array string)
list不支持隨機訪問。


考慮對容器的不同處理,list類支持快速地對中間的元素進行修改,但考慮考慮快速隨機訪問應當對vector類進行。
推薦使用vector 對於在中間插入的情況,推薦對有序時在尾部插入,再利用sort(重載序運算)
無序插入,先對list進行操作,再拷貝到vector中。
在不確定使用哪種容器時,在代碼中多使用迭代器,以爲修改提供方便。


對容器進行初始化時,一般當只有個數時,相應的類要有默認構造函數,也可以沒有,而將除個數外第二個參數指定爲用來初始化的
值,即所謂初始化器。


容器有一些通用的相應類型組件,reference是一個,它與iterator相對,用於保持在容器外修改元素的能力。
當然相應的簡單方法是auto引用。value_type可以表明元素類型。
這些組件多用於泛型編程中。


容器具有構造拷貝的方法,這種形式可以考慮爲構造接口的統一。(如利用向量與矩陣初始化向量)


交換元素的方法爲簡單的swap,在形式上不用拷貝臨時變量進行交換。
常用的對元素操作方法,insert erease(delete) clear emplace(向元素進行寫運算 但是是以構造函數參數的形式 而非直接初始化臨時對象的方法)
對於順序容器的迭代器有自增減能力(forward_list除外)


const_iterator 在配合auto方面是有用的,否則準確的類型推斷是複雜的。


對順序容器元素進行初始化當具有形式C c(,,...)時是有三重含義的,
    構造函數初始化列表,
    若干個具有初始值的元素,
    在兩個迭代器間的拷貝初始化。(這種初始化方法不要求兩個容器元素類型完全相同,只要能進行轉換即可)


上面所提到的類型轉換與類的其它類型初始化是不同的,其多應用於不能直接運算的操作符,進行類型轉換。
其與操作符重載形式相近但不同。其返回值類型在operator後面,沒有參數,具有如下形式: operator int(){return real;}


seq暫時不被Code Blocks所支持 暫時不涉及,而且其初始化方法與上面討論的重複。




對順序容器的初始化,有一個特例,爲array。其在初始化時必須在模板出給出大小:array
且有默認構造函數的類支持初始化空array。


順序容器中除了array外的容器支持assign方法,將容器替換爲相應的形式(是一種完全的替換),其有如下三種形式:
    seq.assign(iterator_begin, iterator_end)
    seq.assign(num, val)
    seq,assign({......})
由於固定大小的限制array不適用assign


swap方法有兩種調用形式,但都是對同一種容器使用的。其速度一般比單純的拷貝快得多。(畢竟相同類型)
swap方法的具體實現對array及其他順序容器是不同的,對其它順序容器可以理解爲換了名稱,而對array,是進行了元素
的拷貝替換,這使得指向容器元素的迭代器在swap前後的表現不同。
對於其它容器,即使進行了swap,指向的元素也是不變的(未交換),對array,指向的元素由於拷貝賦值而改變。
上面的現象對string的操作與array也是一樣的。(string不屬於標準庫容器,已經進行了模板實例化並封裝)


順序容器支持關係運算符比較,其是基於元素封裝的關係運算符的,基本符合類字典序。


順序容器的添加元素操作值得注意的是emplace_back(emplace)
對於插入操作地點的方便記憶方法是,由於迭代器範圍導致的插入必須在前面進行。
插入操作與前面的操作相同支持:範圍插入,多個同值插入,列表插入。(insert)
考慮到性能問題,對於使用insert()的情況,儘量對list進行。
相應地刪除方法:erase(具備單個迭代器刪除及範圍刪除,返回指向刪除後元素的迭代器)、clear


在直接利用erase方法刪除容器中的元素的情況下,但重複刪除時,可能存在迭代器失效的情況,
要使用erase刪除元素,一種比較麻煩的方法需要指定兩個迭代器,一個用於刪除,一個用於指向。
        vectorvi0{1, 1, 5, 6, 5};
        vector::iterator vii = vi0.begin();
        vector::iterator viii = vi0.begin();




        while(viii != vi0.end())
        {
                if ((*vii) == 5)
                {
                        vi0.erase(vii);
                        vii = viii;
                }
                else{
                viii++;
                vii = viii;
                }


        }




另一種可以利用erase的返回值
        vectorvi{10, 11, 12, 9, 8};
        vector::iterator vii = vi.begin();
        while(vii != vi.end())
        {
                if ((*vii) % 2 == 0)
                        vii = vi.erase(vii);
                else
                        ++vii;
        }




由於刪除容易出錯,故推薦使用標準庫algorithm remove方法,remove的具體實現應容器而異,vector remove實現元素移動而非刪除,
list remove實現刪除(解節點)。
對vector當同時使用兩個時(先remove再erase),可以實現元素刪除。


對於改變容器大小的操作可能使迭代器失效的問題,要應用(insert erase等的返回迭代器進行處理)
一般的,對於容器元素的操作導致迭代器失效的情況,需要考慮容器的類型,複製、拷貝、解節點這些數據結構底層特性是需要考慮的。


順序容器提供除了迭代器解引用外的訪問首尾元素的方法:front() back() 當容器爲空時操作的行爲未定義。
對於支持下標運算的容器,類似於dict get方法有相對安全的訪問下標方法(.at(num) 如元素不存在,拋出異常)
這裏在使用類型推斷auto時也要注意改變值要使用引用。
發佈了28 篇原創文章 · 獲贊 23 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章