C++ Primer(第五版) 第九章:順序容器
練習9.1:考察使用哪種順序容器
(a)list,當需要在容器中任意位置執行插入/刪除操作時,用list最好
(b)deque,當需要在頭部插入/刪除元素,不需要在容器中間任意位置插入/刪除元素時,用deque最好
(c)vector,當不需要在頭部/任意位置插入/刪除元素的情況下,用vector最好
練習9.2:考察對容器可以保存的元素類型的限制
list<deque<int>>lst1;
list<deque<int> >lst2; //在編譯器較舊版本中的書寫方式
練習9.3:考察叠代器範圍的條件
它們指向同一個容器中的元素(包括尾元素的下一個位置)
end的位置不在begin之前(可以通過反復遞增使得begin到達end)
練習9.4:考察通過叠代器+循環處理一個元素範圍
1 bool find(vector<int>::iterator begin,vector<int>::iterator end,int value) 2 { 3 //寫法1: 4 while ( begin!=end ) 5 { 6 if ( *begin==value ) return true; 7 ++begin; 8 } 9 //寫法2: 10練習9.4for ( auto iter=begin;iter!=end;++iter ) 11 { 12 if ( *iter==value ) return true; 13 } 14 return false; 15 }
練習9.5:考察同上(當未找到定值時返回尾元素之後的那個位置)
1 vector<int>::iterator find(vector<int>::iterator begin,vector<int>::iterator end,int value) 2 { 3 //練習9.5寫法1: 4 while ( begin!=end ) 5 { 6 if ( *begin==value ) return begin; 7 ++begin; 8 } 9 //寫法2: 10 for ( auto iter=begin;iter!=end;++iter ) 11 { 12 if ( *iter==value ) return iter; 13 } 14 return end; 15 }
練習9.6:考察叠代器支持的操作
兩個叠代器只能判斷是否相等,不能比較大小
1 list<int>lst1; 2 list<int>::iterator iter1=lst1.begin(),lter2=lst1.end(); 3 while ( iter1!=lter2 ) /*……*/練習9.6
練習9.7:考察容器類型成員中的類型別名的使用對象
size_type得到容器大小
練習9.8:考察同上
讀取string的list中的元素,使用const_iterator //只讀不寫
寫入list,使用iterator
練習9.9:考察相似叠代器的區別
begin返回容器的iterator類型
cbegin返回容器的const_iterator類型
練習9.10:考察叠代器的類型
在gcc4.8下這段代碼是錯誤的,因為it1和it2的類型是不一樣的
正確的寫法應該是
1 auto it1 = v1.begin(); 2 auto it2 = v2.begin(), it3 = v1.cbegin(), it4 = v2.cbegin();View Code
即:只有it1是iterator,剩下對象的類型都是const_iterator
練習9.11:考察vector的創建和初始化
1 vector<int>vec1; //不包含值,容器為空 2 vector<int>vec2{1,2,3}; //包含三個值,分別為1,2,3 3 vector<int>vec3={1,2,3}; //包含三個值,分別為1,2,3 4 vector<int>vec4(vec3); //包含三個值,分別為1,2,3 5 vector<int>vec5(vec4.begin(),vec4.end()); //包含三個值,分別為1,2,3 6 vector<int>vec6(3); //包含三個值,均為0 7 vector<int>vec7(3,1); //包含三個值,均為1練習9.11
練習9.12:考察初始化時傳容器和傳兩個叠代器(範圍)的差別
傳容器:要求兩個容器的容器類型和元素類型必須相同,同時得到新容器中元素的範圍和老容器中元素的範圍相同(整個拷貝)
傳兩個叠代器(範圍):不要求容器類型相同,同時只要能將拷貝的元素轉換為要初始化的容器的類型就可以(即新容器和老容器中的元素類型也可以不同),同時可以拷貝容器的子序列(不一定是整個容器)
練習9.13:考察容器的拷貝初始化
1 list<int>lst1; 2 vector<double>vec1(lst1.begin(),lst1.end()); 3 vector<int>vec2; 4 vector<double>vec3(vec2.begin(),vec2.end());練習9.13
練習9.14:考察容器的賦值運算(assign)
1 list<const char*>lst; 2 vector<string>vec; 3 vec.assign(lst.cbegin(),lst.cend());練習9.14
練習9.15:考察相同容器的大小比較
1 vector<int>vec1(3,1); 2 vector<int>vec2(3,2); 3 if ( vec1==vec2 ) cout<<"vec1==vec2"<<endl; 4 else cout<<"vec1!=vec2"<<endl;練習9.15
練習9.16:考察不同容器的大小比較(先統一容器再進行比較)
1 vector<int>vec1(3,1); 2 list<int>lst1(3,2); 3 vector<int>vec2(lst1.begin(),lst1.end()); 4 if ( vec1==vec2 ) cout<<"vec1==lst1"<<endl; 5 else cout<<"vec1!=lst1"<<endl;練習9.16
練習9.17:考察容器比較的限制
c1和c2必須是相同類型的容器,且必須保存相同類型的元素
同時且保存的元素必須支持條件運算符的比較
練習9.18:考察deque的push_back操作
1 deque<string>container; 2 string word; 3 while ( cin>>word ) container.push_back(word); 4 for ( auto iter=container.cbegin();iter!=container.cend();++iter ) cout<<*iter<<endl;練習9.18
練習9.19:考察list的push_back操作及其和dequepush_back操作的不同
list、vector和deque的push_back操作類似,只需要改變一下類型名即可
1 list<string>container; 2 string word; 3 while ( cin>>word ) container.push_back(word); 4 for ( auto iter=container.cbegin();iter!=container.cend();++iter ) cout<<*iter<<endl;練習9.19
練習9.20:考察訪問list和向deque中添加元素
1 list<int>lst{1,2,3,4}; 2 deque<int>odd; 3 deque<int>even; 4 for ( auto i:lst ) 5 { 6 if ( i&1 ) odd.push_back(i); 7 else even.push_back(i); 8 }練習9.20
練習9.21:考察為什麽通過insert的返回值插入這個循環和push_front()等價
1 vector<string>vec; 2 auto iter=vec.begin(); 3 string word; 4 while ( cin>>word ) iter=vec.insert(iter,word);練習9.21
剛開始string被添加到begin的位置,因為insert的返回值指向第一個新加入的元素的叠代器(而新加入的元素加在原來容器中begin所在的元素的前一位置,即新加入的元素變成了當前容器的begin),所以insert的返回值是當前容器的begin,每次都將重復這樣的操作,即每次都往容器頭部加入元素。
練習9.22:考察容器添加元素對叠代器的影響
錯誤:(a)循環將一直進行下次,因為iter永遠無法等於mid
(b)添加元素會使mid這個叠代器失效
修改:在添加元素的過程中不斷更新中間叠代器mid、當前叠代器iter和容器大小
1 vector<int>iv{1,1,1,2,3,4}; 2 int cursor=iv.size()/2; 3 int some_value=1; 4 auto iter=iv.begin(); 5 auto mid=iv.begin()+cursor; 6 while ( iter!=mid ) 7 { 8 if ( *iter==some_value ) 9 { 10 iv.insert(iter,2*some_value); 11 ++iter; 12 ++cursor; 13 mid=iv.begin()+cursor; 14 } 15 }練習9.22
練習9.23:考察當容器中只有一個元素時,訪問首元素和尾元素的結果
val==val2==val3==val4
練習9.24:考察使用不同的方式訪問vector中的第一個元素,以及當容器為空時的訪問結果
1 vector<int>vec; 2 vec.at(0); 3 vec[0]; 4 vec.front(); 5 *(vec.begin());練習9.24
容器為空時,訪問容器中的元素會產生未定義行為
練習9.25:考察刪除多個元素
當elem1==elem2(==尾後叠代器)時,沒有事情發生
當elem1!=尾後叠代器&&elem2==尾後叠代器時,刪除容器中從elem1所在位置的元素往後所有的元素(包括elem1所在位置的元素)
練習9.26:考察從容器內部刪除元素
1 int ia[]={0,1,1,2,3,5,8,13,21,55,89}; 2 vector<int>vec(ia,end(ia)); 3 list<int>lst(vec.begin(),vec.end()); 4 auto iter1=vec.begin(); 5 while ( iter1!=vec.end() ) 6 { 7 if ( (*iter1)&1 ) ++iter1; 8 else iter1=vec.erase(iter1); 9 } 10 auto iter2=lst.begin(); 11 while ( iter2!=lst.end() ) 12 { 13 if ( (*iter2)&1 ) iter2=lst.erase(iter2); 14 else ++iter2; 15 } 16 for ( auto i:vec ) cout<<i<<endl; 17 for ( auto i:lst ) cout<<i<<endl;練習9.26
練習9.27:考察從forward_list中刪除元素
1 forward_list<int>flst={0,1,2,3,4,5,6,7,8,9}; 2 auto prev=flst.before_begin(); 3 auto curr=flst.begin(); 4 while ( curr!=flst.end() ) 5 { 6 if ( (*curr)&1 ) curr=flst.erase_after(prev); 7 else 8 { 9 prev=curr; 10 ++curr; 11 } 12 } 13 for ( auto i:flst ) cout<<i<<endl;練習9.27
練習9.28:考察往forward_list中添加元素
1 void find_and_insert(forward_list<string>& list,const string& to_find,const string& to_add) 2 { 3 auto prev=list.before_begin(); 4 bool change=false; 5 for ( auto curr=list.begin();curr!=list.end();prev=curr++ ) 6 { 7 if ( (*curr)==to_find ) 8 { 9 curr=list.insert_after(curr,to_add); 10 change=true; 11 } 12 } 13 if ( !change ) list.insert_after(prev,to_add); 14 }練習9.28
練習9.29:考察resize的用法
vec.resize(100)會往rec中再添加75個元素,添加元素的值均為0
vec.resize(10)會使得rec中刪除90個元素只保留下前10個元素
練習9.30:考察resize對容器初始化的影響
當元素的類型是類時,同時類的構造函數沒有提供初始值時不能接受單個參數的resize
練習9.31:考察vector、list和forward_list叠代器操作的不同
list的叠代器沒有+=2這樣的操作,改成advance(iter,2),和+=2等價
1 list<int>vi={0,1,2,3,4,5,6,7,8,9}; 2 auto iter=vi.begin(); 3 while ( iter!=vi.end() ) 4 { 5 if ( (*iter)&1 ) 6 { 7 iter=vi.insert(iter,*iter); 8 advance(iter,2); 9 } 10 else iter=vi.erase(iter); 11 } 12 for ( auto i:vi ) cout<<i<<endl;list
forword_list中重點註意兩個叠代器位置的變化
1 forward_list<int>vi={0,1,2,3,4,5,6,7,8,9}; 2 auto prev=vi.before_begin(); 3 auto iter=vi.begin(); 4 while ( iter!=vi.end() ) 5 { 6 if ( (*iter)&1 ) 7 { 8 iter=vi.insert_after(prev,*iter); 9 advance(iter,2); 10 advance(prev,2); 11 } 12 else iter=vi.erase_after(prev); 13 } 14 for ( auto i:vi ) cout<<i<<endl;forward_list
練習9.32:考察程序規範
不合法,當執行完insert後,iter進行的操作取決於編譯器是不確定的。更詳細的討論請看:https://github.com/Mooophy/Cpp-Primer/issues/125
練習9.33:考察insert的返回值(返回插入的第一個新添加的元素)
insert使得vector的存儲空間被重新分配,導致叠代器失效,會造成程序崩潰
練習9.34:考察insert的返回值
會造成無限循環,當訪問的一個奇數時,會無限訪問該奇數,因為程序遇到奇數會在奇數前添加一個元素,同時叠代器指向該奇數的前一個元素,當叠代器++時又是該奇數
1 #include<iostream> 2 #include<vector> 3 using namespace std; 4 5 int main() 6 { 7 vector<int>vi={0,1,2,3,4,5,6,7,8,9}; 8 auto iter=vi.begin(); 9 while ( iter!=vi.end() ) 10 { 11 if ( (*iter)&1 ) 12 { 13 iter=vi.insert(iter,*iter); 14 ++iter; 15 } 16 ++iter; 17 } 18 for ( auto i:vi ) cout<<i<<endl; 19 }練習9.34
C++ Primer(第五版) 第九章:順序容器