【C/C++開發】容器set和multiset,C++11對vector成員函數的擴展(cbegin()、cend()、crbegin()、crend()、emplace()、data())

1、set和multiset基礎css

set和multiset會根據特定的排序準則,自動將元素進行排序。不一樣的是後者容許元素重複而前者不容許。html


須要包含頭文件:java

#include <set>ios

set和multiset都是定義在std空間裏的類模板:less

[cpp]  view plain  copy
 print ?
  1. template<class _Kty,  
  2.     class _Pr = less<_Kty>,  
  3.     class _Alloc = allocator<_Kty> >  
  4. class set  
[cpp]  view plain  copy
 print ?
  1. template<class _Kty,  
  2.     class _Pr = less<_Kty>,  
  3.     class _Alloc = allocator<_Kty> >  
  4. class multiset  

只要是可復賦值、可拷貝、能夠根據某個排序準則進行比較的型別均可以成爲它們的元素。第二個參數用來定義排序準則。缺省準則less是一個仿函數,以operator<對元素進行比較。

所謂排序準則,必須定義strict weak ordering,其意義以下:ide

一、必須使反對稱的。svg

對operator<而言,若是x<y爲真,則y<x爲假。函數

二、必須使可傳遞的。性能

對operator<而言,若是x<y爲真,且y<z爲真,則x<z爲真。ui

三、必須是非自反的。

對operator<而言,x<x永遠爲假。

由於上面的這些特性,排序準則能夠用於相等性檢驗,就是說,若是兩個元素都不小於對方,則它們相等。


2、set和multiset的功能

和全部關聯式容器相似,一般使用平衡二叉樹完成。事實上,set和multiset一般以紅黑樹實做而成。

自動排序的優勢是使得搜尋元素時具備良好的性能,具備對數時間複雜度。可是形成的一個缺點就是:

不能直接改變元素值。由於這樣會打亂原有的順序。

改變元素值的方法是:先刪除舊元素,再插入新元素。

存取元素只能經過迭代器,從迭代器的角度看,元素值是常數。


3、操做函數

構造函數和析構函數

set的形式能夠是:


有兩種方式能夠定義排序準則:

一、以template參數定義:

[cpp]  view plain  copy
 print ?
  1. set<int,greater<int>> col1;  
此時,排序準則就是型別的一部分。型別系統確保只有排序準則相同的容器才能被合併。

程序實例:

[cpp]  view plain  copy
 print ?
  1. #include <iostream>  
  2. #include <set>  
  3. using namespace std;  
  4.   
  5. int main()  
  6. {  
  7.     set<int> s1;  
  8.     set<int,greater<int> > s2;  
  9.   
  10.     for (int i = 1;i < 6;++i)  
  11.     {  
  12.         s1.insert(i);  
  13.         s2.insert(i);  
  14.     }  
  15.     if(s1 == s2)  
  16.         cout << "c1 equals c2 !" << endl;  
  17.     else  
  18.         cout << "c1 not equals c2 !" << endl;  
  19. }  
程序運行會報錯。可是若是把s1的排序準則也指定爲greater<int>便運行成功。

二、以構造函數參數定義。

這種狀況下,同一個型別能夠運用不一樣的排序準則,而排序準則的初始值或狀態也能夠不一樣。若是執行期纔得到排序準則,並且須要用到不一樣的排序準則,這種方式能夠派上用場。

程序實例:

[cpp]  view plain  copy
 print ?
  1. #include <iostream>  
  2. #include "print.hpp"  
  3. #include <set>  
  4. using namespace std;  
  5.   
  6. template <class T>  
  7. class RuntimeCmp{  
  8. public:  
  9.     enum cmp_mode{normal,reverse};  
  10. private:  
  11.     cmp_mode mode;  
  12. public:  
  13.     RuntimeCmp(cmp_mode m = normal):mode(m){}  
  14.   
  15.     bool operator()(const T &t1,const T &t2)  
  16.     {  
  17.         return mode == normal ? t1 < t2 : t2 < t1;  
  18.     }  
  19.   
  20.     bool operator==(const RuntimeCmp &rc)  
  21.     {  
  22.         return mode == rc.mode;  
  23.     }  
  24. };  
  25.   
  26. typedef set<int,RuntimeCmp<int> > IntSet;  
  27.   
  28. void fill(IntSet& set);  
  29.   
  30. int main()  
  31. {  
  32.     IntSet set1;  
  33.     fill(set1);  
  34.     PRINT_ELEMENTS(set1,"set1:");  
  35.   
  36.     RuntimeCmp<int> reverse_order(RuntimeCmp<int>::reverse);  
  37.   
  38.     IntSet set2(reverse_order);  
  39.     fill(set2);  
  40.     PRINT_ELEMENTS(set2,"set2:");  
  41.   
  42.     set1 = set2;//assignment:OK  
  43.     set1.insert(3);  
  44.     PRINT_ELEMENTS(set1,"set1:");  
  45.   
  46.     if(set1.value_comp() == set2.value_comp())//value_comp <span style="font-family: verdana, arial, helvetica, sans-serif; ">Returns the comparison object associated with the container</span>  
  47.         cout << "set1 and set2 have the same sorting criterion" << endl;  
  48.     else  
  49.         cout << "set1 and set2 have the different sorting criterion" << endl;  
  50. }  
  51.   
  52. void fill(IntSet &set)  
  53. {  
  54.     set.insert(4);  
  55.     set.insert(7);  
  56.     set.insert(5);  
  57.     set.insert(1);  
  58.     set.insert(6);  
  59.     set.insert(2);  
  60.     set.insert(5);  
  61. }  
運行結果:


雖然set1和set2的而比較準則自己不一樣,可是型別相同,因此能夠進行賦值操做。


非變更性操做

注意:元素比較操做只能用於型別相同的容器。

特殊的搜尋函數

賦值

賦值操做兩端的容器必須具備相同的型別,可是比較準則自己能夠不一樣,可是其型別必須相同。若是比較準則的不一樣,準則自己也會被賦值或交換。


迭代器相關函數


元素的插入和刪除

注意:插入函數的返回值不徹底相同。

set提供的插入函數:

[cpp]  view plain  copy
 print ?
  1. pair<iterator,bool> insert(const value_type& elem);   
  2. iterator  insert(iterator pos_hint, const value_type& elem);   
multiset提供的插入函數:

[cpp]  view plain  copy
 print ?
  1. iterator  insert(const value_type& elem);   
  2. iterator  insert(iterator pos_hint, const value_type& elem);  
返回值型別不一樣的緣由是set不容許元素重複,而multiset容許。當插入的元素在set中已經包含有一樣值的元素時,插入就會失敗。因此set的返回值型別是由pair組織起來的兩個值:

第一個元素返回新元素的位置,或返回現存的同值元素的位置。第二個元素表示插入是否成功。

set的第二個insert函數,若是插入失敗,就只返回重複元素的位置!

可是,全部擁有位置提示參數的插入函數的返回值型別是相同的。這樣就確保了至少有了一個通用型的插入函數,在各類容器中有共通接口。


注意:還有一個返回值不一樣的狀況是:做用於序列式容器和關聯式容器的erase()函數:

序列式容器的erase()函數:

[cpp]  view plain  copy
 print ?
  1. iterator erase(iterator pos);   
  2. iterator erase(iterator beg, iterator end);  
關聯式容器的erase()函數:

[cpp]  view plain  copy
 print ?
  1. void     erase(iterator pos);   
  2. void     erase(iterator beg, iterator end);   
這徹底是爲了性能的考慮。由於關聯式容器都是由二叉樹實現,搜尋某元素並返回後繼元素可能很費時。

5、set應用示例:

[cpp]  view plain  copy
 print ?
  1. #include <iostream>  
  2. #include <set>  
  3. using namespace std;  
  4.   
  5. int main()  
  6. {  
  7.     typedef set<int,greater<int> > IntSet;  
  8.     IntSet s1;  
  9.   
  10.     s1.insert(4);  
  11.     s1.insert(3);  
  12.     s1.insert(5);  
  13.     s1.insert(1);  
  14.     s1.insert(6);  
  15.     s1.insert(2);  
  16.     s1.insert(5);  
  17.     //the inserted element that has the same value with a element existed is emitted  
  18.   
  19.     copy(s1.begin(),s1.end(),ostream_iterator<int>(cout," "));  
  20.     cout << endl << endl;  
  21.   
  22.     pair<IntSet::iterator,bool> status = s1.insert(4);  
  23.     if(status.second)  
  24.         cout << "4 is inserted as element "  
  25.         << distance(s1.begin(),status.first) + 1 << endl;  
  26.     else  
  27.         cout << "4 already exists in s1" << endl;  
  28.     copy(s1.begin(),s1.end(),ostream_iterator<int>(cout," "));  
  29.     cout << endl << endl;  
  30.   
  31.     set<int>  s2(s1.begin(),s1.end());//default sort criterion is less<  
  32.     copy(s2.begin(),s2.end(),ostream_iterator<int>(cout," "));  
  33.     cout << endl << endl;  
  34. }  

上述程序最後新產生一個set:s2,默認排序準則是less。以s1的元素做爲初值。

注意:s1和s2有不一樣的排序準則,因此他們的型別不一樣,不能直接進行相互賦值或比較。

運行結果:

Defined in header  <iterator>
   
  (1)  
template< class C > 
auto rbegin( C& c ) -> decltype(c.rbegin());
(since C++14) 
(until C++17)
template< class C > 
constexpr auto rbegin( C& c ) -> decltype(c.rbegin());
(since C++17)
  (1)  
template< class C > 
auto rbegin( const C& c ) -> decltype(c.rbegin());
(since C++14) 
(until C++17)
template< class C > 
constexpr auto rbegin( const C& c ) -> decltype(c.rbegin());
(since C++17)
  (2)  
template< class T, size_t N > 
reverse_iterator<T*> rbegin( T (&array)[N] );
(since C++14) 
(until C++17)
template< class T, size_t N > 
constexpr reverse_iterator<T*> rbegin( T (&array)[N] );
(since C++17)
  (3)  
template< class C > 
auto crbegin( const C& c ) -> decltype(std::rbegin(c));
(since C++14) 
(until C++17)
template< class C > 
constexpr auto crbegin( const C& c ) -> decltype(std::rbegin(c));
(since C++17)
     

Returns an iterator to the reverse-beginning of the given container c or array array.

1) Returns a possibly const-qualified iterator to the reverse-beginning of the container  c.
2) Returns  std::reverse_iterator<T*> to the reverse-beginning of the array  array.
3) Returns a const-qualified iterator to the reverse-beginning of the container  c.

range-rbegin-rend.svg

Parameters

c - a container with a rbegin method
array - an array of arbitrary type

Return value

An iterator to the reverse-beginning of c or array

Notes

In addition to being included in <iterator>std::rbegin and std::crbegin are guaranteed to become available if any of the following headers are included: <array><deque><forward_list><list><map><regex><set><string>, <string_view> (since C++17)<unordered_map><unordered_set>, and <vector>.

Overloads

Custom overloads of rbegin may be provided for classes that do not expose a suitable rbegin() member function, yet can be iterated. The following overload is already provided by the standard library:

specializes std::rbegin 
(function)

Example

     
     
     
     
#include <iostream>
#include <vector>
#include <iterator>
 
int main ( )
{
std::vector < int > v = { 3 , 1 , 4 } ;
auto vi = std :: rbegin ( v ) ;
std::cout << * vi << '\n' ;
 
int a [ ] = { - 5 , 10 , 15 } ;
auto ai = std :: rbegin ( a ) ;
std::cout << * ai << '\n' ;
}

Output:

     
     
     
     
4
15

因此這篇博客就是想羅列一下C++11對vector容器的擴充。

std::vector::cbegin和std::vector::cend
這兩個方法是與std::vector::begin和std::vector::end相對應的,從字面就能看出來,多了一個’c’,顧名思義就是const的意思。
因此:
std::vector::cbegin:  Returns a const_iterator pointing to the first element in the container.
std::vector::cend:  Returns a const_iterator pointing to the past-the-end element in the container.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<code class = "hljs cpp" >#include <iostream>
#include <vector>
 
int main ()
{
   std::vector< int > myvector = { 10 , 20 , 30 , 40 , 50 };
 
   std::cout << "myvector contains:" ;
 
   for (auto it = myvector.cbegin(); it != myvector.cend(); ++it)
     std::cout << ' ' << *it;
   std::cout << '\n' ;
 
   return 0 ;
}
Output:
myvector contains: 10 20 30 40 50 </ int ></vector></iostream></code>

std::vector::crbegin和std::vector::crend
這兩個方法就不解釋了,與上面的相比就是多了個’r’, reverse的縮寫,反轉迭代器,代碼就省略了。

std::vector::emplace
以前已經對emplace_back進行了討論,其實還有一個方法叫emplace。
我想說的就是,emplace之於emplace_back就像insert之於push_back。
看英文描述就直觀:

emplace:Construct and insert element
emplace_back:Construct and insert element at the end

如何使用:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<code class = "hljs cpp" >#include <iostream>
#include <vector>
 
int main ()
{
   std::vector< int > myvector = { 10 , 20 , 30 };
 
   auto it = myvector.emplace ( myvector.begin()+ 1 , 100 );
   myvector.emplace ( it, 200 );
   myvector.emplace ( myvector.end(), 300 );
 
   std::cout << "myvector contains:" ;
   for (auto& x: myvector)
     std::cout << ' ' << x;
   std::cout << '\n' ;
 
   return 0 ;
}
Output:
myvector contains: 10 200 100 20 30 300 </ int ></vector></iostream></code>

std::vector::data
Returns a direct pointer to the memory array used internally by the vector to store its owned elements.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<code class = "hljs cpp" >#include <iostream>
#include <vector>
 
int main ()
{
   std::vector< int > myvector ( 5 );
   int * p = myvector.data();
   *p = 10 ;
   ++p;
   *p = 20 ;
   p[ 2 ] = 100 ;
   std::cout << "myvector contains:" ;
   for (unsigned i= 0 ; i<myvector.size(); ++i)= "" std::cout= "" <<= "" '="" myvector[i];="" ' \n';= "" return = "" 0 ;= "" }= "" output:= "" myvector= "" contains:= "" 10 = "" 20 = "" 0 = "" 100 = "" 0 </ int ></vector></iostream></code>

std::vector::shrink_to_fit
Requests the container to reduce its capacity to fit its size.
就是減小空間

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<code class = "hljs cpp" ><code class = "hljs cpp" >#include <iostream>
#include <vector>
int main ()
{
   std::vector< int > myvector ( 100 );
   std::cout << "1. capacity of myvector: " << myvector.capacity() << '\n' ;
   std::cout << "1. size of myvector: " << myvector.size() << '\n' ;
 
   myvector.resize( 10 );
   std::cout << "2. capacity of myvector: " << myvector.capacity() << '\n' ;
   std::cout << "2. size of myvector: " << myvector.size() << '\n' ;
 
   myvector.shrink_to_fit();
   std::cout << "3. capacity of myvector: " << myvector.capacity() << '\n' ;
  std::cout << "3. size of myvector: " << myvector.size() << '\n' ;
   return 0 ;
}
//輸出
1 . capacity of myvector: 100
1 . size of myvector: 100
2 . capacity of myvector: 100
2 . size of myvector: 10
3 . capacity of myvector: 10
3 . size of myvector: 10 </ int ></vector></iostream></code></code>

此時,就是要明白size和capacity的區別,也就會更加理解resize和reserve的區別了!

相關文章
相關標籤/搜索