【計算機本科補全計劃】C++ Primer:String Vector標準庫及迭代器的使用

HustWolfzzb發表於2017-10-08

正文之前

今天幫學妹選了一天的電腦配件,然後從中領悟:坐看狗東黑我錢,任他漲價我不動!!最後果斷的用學妹的錢衝了個Plus,然後領了一堆券,最後學妹還省了30塊,另外昨晚找到了一個買酷睿i7 7700k 送主機板的,又省了250,然後各種大小活動都去看了個遍,總算是學會了怎麼省錢,特此還要多謝我華科電信的一位大佬--“大王”巍的指點,不然我肯定被狗東坑到懷疑人生。看配置的直接翻到正文之後看!


正文


1、 名稱空間using宣告

我們自己平時用的時候是:

using namespace std;複製程式碼

老實說我也就會這一句,所以其他的基本漠不關心,但是既然書裡講了我也就寫出來了。一般的格式如下:

using std::cin;複製程式碼

有了上面這句,以後寫cin就只是cin了。不需要std::cin了。不過也僅限於此,cout還是沒法直接用,不過這樣的好處就是可以單獨的引用,不會造成浪費(Maybe)?

另外標頭檔案不應該包含著using宣告,因為標頭檔案的內容會被拷貝到所有引用他的檔案中取去,如果標頭檔案內有這個using宣告,那麼每一個使用了該檔案的檔案就會有這個宣告,對於某些程式來說,由於不經意間包含了一些名字,反而可能會產生始料未及的名字衝突。


2、 標準庫 string的基本操作

首先,要使用string這個標準庫就要宣告下標頭檔案

#include <string>
using std::string複製程式碼
  • string的初始化
    有以下幾種種方式

    string s1;    //s1:empty
    string s2=s1;    //s2: empty
    string s3="string";    //s3:string\0
    string s4(10,'c')   //s4:cccccccccc
    string s5("string");    //s5:string\0複製程式碼

    幾種方式的過程和效果我都寫出來了,就不多贅述了。

  • string物件的上的操作

操作形式 效果
cout<<s 標準輸出流。輸出整個字串
cin>>s 從cin標準輸入流中一次讀取,直到遇到空白符號停下
getline(cin,s) 從cin標準輸入流中一次讀取一行,不到換行符不停止讀取,可以讀空白
s.empty 判斷是否為空,為空則返回true(bool)
s.size() 直接讀取字串的字元個數,包括’\0‘
s[n] 直接讀取s這個字串中第n個字元,從0開始計算
s1+s2 連線兩個字串
s1=s2 拷貝賦值
s1==s2 判斷兩個字串是否相等,相等則返回true(bool)
s1!=s2 判斷兩個字串是否不等,不等則返回true(bool)
<,>,<=,>= 關係運算子,進行字串的大小比較,後面說
  • 下面詳細說明各個操作:

    • 讀寫操作,讀取的時候自動忽略開頭的空白,遇到第一個非空字元開始讀取,之後遇到第一個空白字元就停止讀取,不管後面還有沒有,也不讀取空白字元,輸入“ hello world !!”,最後在cin>>s 中讀取到的只有s=“hello\0”;

    • 讀取操作可以多個物件一起進行:cin>>s1>>s2; 那麼“ hello world !!”最後的結果是:s1=“hello”;s2=“world”;

    • 使用getline(cin,s)讀取一整行,getline()是無返回值函式,直接對s進行操作,所以不需要額外的空間承載返回值;getline()見到"\0"就結束輸入,但是得到的字串不含有換行符,所以如果要按行輸出,那麼就要自己手動加上換行操作。

    • empty() ,size()操作都是很顯而易見的,帶有返回值的函式,而且不需要傳入引數,是string自帶的一個成員函式,只需要使用點操作符呼叫即可:s.empty() s.size() ,前者返回一個bool變數,後者直接返回一個string::type_size的無符號整數變數,它能夠存放下任何string的大小,是其專屬的大小變數。若要在外部定義string::type_size型別的變數,可以用auto或者是decltype得到。
      auto len=s.size(); decltype(s.size()) len;
    • 比較運算子其實也沒啥差別,但是比較大小的話,string中有一套規則(但是我自己總結下來:只要從頭開始看起,第一個不相同的對應位置的字元比較大小就全權代表了這個字串的大小,另外結束符號小於一切的字元!通用,不信你看):
      * 如果兩個string長度不同,較短的string物件每個字元都與較長的物件對應部分相同,那麼較短的就短於長的;(like:abc < abcd )
      * 如果在相應的位置不一樣,那麼就比較第一個相異的字元;(like: abd > abcd )複製程式碼
    • 相加相減操作:就直接加上咯,相當於拼接,反正string是動態增長長度的,所以隨便你加多少。還有一種騷操作,那就是直接用原本的字串變數直接加上一個字串:但是記住,加號兩邊一定要有一個是string變數,兩個字串字面值直接加起來是違法操作!``` string s2=s1+"zhang"+"z"+"b" //從左到右相加,所以左邊一直都是string變數!!


3、 處理string物件中的字元

字元操作形式 效果(s指string物件中的單個字元)
isalnum(s) 判斷字元是否是字母或數字;
isalpha(s) 判斷字元是否是字母;
iscntrl(s) 判斷字元是否是控制字元;
isdigit(s) 判斷字元是否是數字;
isgraph(s) 判斷字元是否是可列印的非空格字元;
ispunct(s) 判斷字元是否是標點符號;
isspace(s) 判斷字元是否是空白字元;
isupper(s) 判斷字元是否是大寫字母;
isxdigit(s) 判斷字元是否是十六進位制數;
toupper(s) 轉換為大寫字母;
tolower(s) 轉換為小寫字母。

採用基於範圍的for迴圈實現:

for ( auto c:str)  //此句的意思是:對於str中的每個字元進行拷貝
  cout<<c<<endl;複製程式碼

上述只能實現讀而不能實現存,因為是直接拷貝,所以無法對string的單個字元進行操作,但是如果是引用,就可以實現了。

for ( auto &c:str)  //此句的意思是:對於str中的每個字元進行引用
{
  cout<<c<<endl;
  c=toupper(c);
}複製程式碼

利用這些特性,結合一個大家常見的下標運算子(這個點就不講了,跟陣列一樣,爛大街了),我自己實現了一個把所有的英文單詞的首字母改成大寫的程式:

string w="hello boy I don't want to hurt you? baby~~~";
w[0]=toupper(w[0]);
for (decltype(w.size()) index=0; index < w.size(); ++index)
{
    if (isspace(w[index]))
    {
        w[index+1]=toupper(w[index+1]);
    }
}
cout<<"================================================\n"<<w<<"\n================================================\n"<<endl;複製程式碼


4、 標準庫型別Vector定義與初始化

  • vector是一種物件的集合,可以看作是一種容易,好比是房子,前面的string可以看做是教室排座位,每個座位上按序號只能坐入字元,不能是別的型別,並且多個字元可以組成一個小組,string物件就是一個小組;而vector就好比是大樓,可以安置的型別更加寬廣。可以放入int,string,char等一些型別。因為在具體定義一個vector物件前不知道型別,所以我們稱之為類别範本,對其實施建立的時候稱此過程為例項化,定義如下:
    vector<int> ivec;
    vector<string> strvec;
    vector<vector<int> > vecivec;複製程式碼
    因為vector是容納物件的,所以不存在包含引用的vector;初始化如下:
    vector<T> tvec;   // Empty vector
    vector<T> tvec(v1);  //  copy v1 to tvec
    vector<T> tvec = v1 ; // 同上
    vector<T> tvec(n,val); //tvec包含了n個相同T型別的val元素;
    vector<T> tvec(n); //執行n次空的初始化;
    vector<T> tvec{a,b,c,d  ···};  //具體的初始化
    vector<T> tvec={a,b,c,d ···};  //同上複製程式碼
    注意如果是拷貝初始化的話,不同型別的vector是不能相互拷貝的!

5、 向vector中新增元素

對於vector這個容器,只能用專用的內建函式來對其增加元素,push_back具體用法如下:

vector<int> ivec;
for(int i=0;i!=100;++i)
{
  ivec.push_back(i);
}複製程式碼

從上面我們可以知道,vector具有良好的動態增長的效能,所以一開始如果就限定其大小的話,是一種對特性的浪費,一開始初始化的時候就限定容量是不是一件明智的事情!(注意,其實上面的操作不是很符合規定,因為隱性規定:在迴圈體中改變了遍歷序列的長度的操作不用for 迴圈,可能造成緩衝區溢位)


6、 其他vector操作

彙總的Vector操作,假設c是vector變數 效果
c.clear() 移除容器中所有資料。
c.empty() 判斷容器是否為空。
c.erase(pos) 刪除pos位置的資料
c.erase(beg,end) 刪除[beg,end)區間的資料
c.front() 傳回第一個資料。
c.insert(pos,elem) 在pos位置插入一個elem拷貝
c.pop_back() 刪除最後一個資料。
c.push_back(elem) 在尾部加入一個資料。
c.resize(num) 重新設定該容器的大小
c.size() 回容器中實際資料的個數。
c.begin() 返回指向容器第一個元素的迭代器
c.end() 返回指向容器最後一個元素的迭代器

我們可以看到,對於vector只有一種新增元素的辦法,但是對於元素的讀寫,可以直接用下標表示法,這個陣列,string是完全共同的, 但是請注意,千萬不要給定一個不存在的下標,如果超出了vector變數的長度,那麼毫無疑問你的,會產生嚴重錯誤!甚至可能導致 快取區溢位!!


7、 迭代器

  • 迭代器的介紹
    • 迭代器類似於指標型別,它也提供了對物件的間接訪問。
    • 指標是c語言中就有的東西,迭代器是c++中才有的,指標用起來靈活高效,迭代器功能更豐富些。
    • 迭代器提供一個對容器物件或者string物件的訪問的方法,並且定義了容器範圍。
  • 對於上面介紹的幾種標準庫型別,都有內建的迭代器操作,所謂迭代器,就是兩個地址。比如說下面的例子:

    vector<int > v;
    auto b=v.begin();複製程式碼

    此時如果可以檢視b的型別,你會發現其實就是個指標物件。只是其型別由編譯器給定。我們只管auto 或者 decltype就好了!,另外還有一個end()函式返回尾後迭代器,沒有什麼實際意義,正如名字,是在最後一個元素的下一個位置,用於判斷是否為空的容器(begin end指向一個位置的時候)

  • 下面是一些關於迭代器的操作,其中iter就是迭代器,跟指標其實沒啥區別

  • 每種容器型別都定義了自己的迭代器型別,如vector

    vector<int>::iterator iter;複製程式碼

    語句定義了一個名為 iter 的變數,它的資料型別是 vector 定義的 iterator 型別。每個標準庫容器型別都定義了一個名為 iterator 的成員,這裡的 iterator 與迭代器實際型別的含義相同。

  • 前面的程式用vector::iterator 改變 vector 中的元素值。每種容器型別還定義了一種名為 const_iterator 的型別,該型別只能用於讀取容器內元素,但不能改變其值。​ 當我們對普通 iterator 型別解引用時,得到對某個元素的非 const。而如果我們對const_iterator 型別解引用時,則可以得到一個指向 const 物件的引用,如同任何常量一樣,該物件不能進行重寫。

for (vector<string>::const_iteratoriter = text.begin();iter != text.end(); ++iter)
    cout << *iter << endl; // printeach element in text複製程式碼

​ 使用 const_iterator 型別時,我們可以得到一個迭代器,它自身的值可以改變,但不能用來改變其所指向的元素的值。可以對迭代器進行自增以及使用解引用操作符來讀取值,但不能對該元素賦值。

  • 使vector物件的迭代器失效的操作
    • for中新增元素
    • push_back或者改變容量的操作

記住一點:但凡是使用了迭代器的迴圈體,此時就不要像迭代器所屬的容器進行新增元素的操作了!!!千萬不要!!

  • 迭代器的算術操作(跟指標沒差別,只是是標準庫自帶的型別)
    iter + n
    iter - n
    iter1 - iter2
    vector<int>::iterator mid = vi.begin() +vi.size() / 2;複製程式碼
    任何改變 vector 長度的操作都會使已存在的迭代器失效。例如,在呼叫 push_back 之後,就不能再信賴指向 vector 的迭代器的值了!!!!!!!~

正文之後

我只能看著這個配置流口水啊!!具體的購買詳細和指導請看我另一篇文:萬元桌上型電腦組裝養成記


後來又加了三件配個套:

相關文章