【C/C++开发】容器set和multiset,C++11对vector成员函数的扩展(cbegin()、cend()、crbegin()、crend()、emplace()、data())
一、set和multiset基础
set和multiset会根据特定的排序准则,自动将元素进行排序。不同的是后者允许元素重复而前者不允许。
需要包含头文件:
#include <set>
set和multiset都是定义在std空间里的类模板:
只要是可复赋值、可拷贝、可以根据某个排序准则进行比较的型别都可以成为它们的元素。第二个参数用来定义排序准则。缺省准则less是一个仿函数,以operator<对元素进行比较。
所谓排序准则,必须定义strict weak ordering,其意义如下:
1、必须使反对称的。
对operator<而言,如果x<y为真,则y<x为假。
2、必须使可传递的。
对operator<而言,如果x<y为真,且y<z为真,则x<z为真。
3、必须是非自反的。
对operator<而言,x<x永远为假。
因为上面的这些特性,排序准则可以用于相等性检验,就是说,如果两个元素都不小于对方,则它们相等。
二、set和multiset的功能
和所有关联式容器类似,通常使用平衡二叉树完成。事实上,set和multiset通常以红黑树实作而成。
自动排序的优点是使得搜寻元素时具有良好的性能,具有对数时间复杂度。但是造成的一个缺点就是:
不能直接改变元素值。因为这样会打乱原有的顺序。
改变元素值的方法是:先删除旧元素,再插入新元素。
存取元素只能通过迭代器,从迭代器的角度看,元素值是常数。
三、操作函数
构造函数和析构函数
set的形式可以是:
有两种方式可以定义排序准则:
1、以template参数定义:
此时,排序准则就是型别的一部分。型别系统确保只有排序准则相同的容器才能被合并。
程序实例:
程序运行会报错。但是如果把s1的排序准则也指定为greater<int>便运行成功。
2、以构造函数参数定义。
这种情况下,同一个型别可以运用不同的排序准则,而排序准则的初始值或状态也可以不同。如果执行期才获得排序准则,而且需要用到不同的排序准则,这种方式可以派上用场。
程序实例:
运行结果:
虽然set1和set2的而比较准则本身不同,但是型别相同,所以可以进行赋值操作。
非变动性操作
注意:元素比较操作只能用于型别相同的容器。
特殊的搜寻函数
赋值
赋值操作两端的容器必须具有相同的型别,但是比较准则本身可以不同,但是其型别必须相同。如果比较准则的不同,准则本身也会被赋值或交换。
迭代器相关函数
元素的插入和删除
注意:插入函数的返回值不完全相同。
set提供的插入函数:
multiset提供的插入函数:
返回值型别不同的原因是set不允许元素重复,而multiset允许。当插入的元素在set中已经包含有同样值的元素时,插入就会失败。所以set的返回值型别是由pair组织起来的两个值:
第一个元素返回新元素的位置,或返回现存的同值元素的位置。第二个元素表示插入是否成功。
set的第二个insert函数,如果插入失败,就只返回重复元素的位置!
但是,所有拥有位置提示参数的插入函数的返回值型别是相同的。这样就确保了至少有了一个通用型的插入函数,在各种容器中有共通接口。
注意:还有一个返回值不同的情况是:作用于序列式容器和关联式容器的erase()函数:
序列式容器的erase()函数:
关联式容器的erase()函数:
这完全是为了性能的考虑。因为关联式容器都是由二叉树实现,搜寻某元素并返回后继元素可能很费时。
五、set应用示例:
上述程序最后新产生一个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
.
c
.c
.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:
(C++14)
|
specializes std::rbegin (function) |
Example
#include <iostream>#include <vector>#include <iterator>int main(){auto vi = std::rbegin(v);int a[] = { -5, 10, 15 };auto ai = std::rbegin(a);}
Output:
415
所以这篇博客就是想罗列一下C++11对vector容器的扩充。
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> |
如何使用:
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> |
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> |
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的区别了!