C++ STL set和multiset

1、简介

  set 是关联容器的一种,是排序好的集合(元素已经进行了排序)。set 和 multiset 类似,它和 multiset 的差别在于 set 中不能有重复的元素。multiset 的成员函数 set 中也都有。
  不能直接修改 set 容器中元素的值。因为元素被修改后,容器并不会自动重新调整顺序,于是容器的有序性就会被破坏,再在其上进行查找等操作就会得到错误的结果。因此,如果要修改 set 容器中某个元素的值,正确的做法是先删除该元素,再插入新元素。
使用 set 必须包含头文件 <set>。

2、set的定义

  template < class Key, class Pred = less<Key>, class A = allocator<Key> > class set {...}
由于不能有重复元素,所以 set 中插入单个元素的 insert 成员函数与 multiset 中的有所不同,其原型如下:
  pair<iterator, bool> insert(const T & val);
如果 set 的 insert 成员函数的返回值是 pair 模板类对象 x,如果 x.second 为 true,则说明插入成功,此时 x.first 就是指向被插入元素的迭代器;如果 x.second 为 false,则说明要插入的元素已在容器中,此时 x.first 就是指向原有那个元素的迭代器。

关联容器的 equal_range 成员函数的返回值也是 pair 模板类对象,其原型如下:
  pair<iterator, iterator> equal_range(const T & val);
返回值对象中的 first 就是 lower_bound 的值,second 就是 upper_bound 的值。

3、multiset简介

  multiset 是关联容器的一种,是排序好的集合(元素已经进行了排序),并且允许有相同的元素。

  不能直接修改 multiset 容器中元素的值。因为元素被修改后,容器并不会自动重新调整顺序,于是容器的有序性就会被破坏,再在其上进行查找等操作就会得到错误的结果。因此,如果要修改 multiset 容器中某个元素的值,正确的做法是先删除该元素,再插入新元素。
使用 multiset 必须包含头文件 <set>。

4、multiset 类模板的定义

template <class Key, class Pred = less<Key>, class B = allocator<Key> > class multiset {
  ...
};
  该模板有三个类型参数:Key、Pred 和 B。类型参数可以有默认值,默认值就是某种类型。例如,Pred 类型参数的默认值就是 less<Key> 类型,B 的默认值就是 allocator<Key> 类型。第三个类型参数极少用到,一般都用默认值。
  multiset 内部在排序时定义了一个变量Pred op,根据表达式op(x, y)来比较两个元素 x、y 的大小。该表达式的值为 true,则说明 x 比 y 小。Pred 的默认值是 less<Key>,less 是 STL 中的函数对象类模板,其定义如下:
template <class_Tp>

struct less
{
  bool operator() (const _Tp &__x, const _Tp &__y) const
  {

    return __x < __y;

  }
};

在默认情况下,multiset 容器中的元素是用<运算符比较大小的。例如,假设 A 是一个类的名字,可以定义一个如下的容器对象:
  multiset <A> s;
由于 multiset 的类型参数可以使用默认值,因此上面的语句等价于:
  multiset < int, less<A>, allocator<A> > s;

模板类 multiset < A, less<A>, allocator<A> > 的 insert 成员函数可以用来插入一个元素。 插入过程中需要进行元素之间的比较,可以认为 insert 成员函数中定义了一个变量 less <A> op,用 op(x, y) 来比较元素 x、y 的大小。归根到底,还是用<运算符比较 x、y 的大小。 因此,<运算符必须经过适当重载,才可以向 multiset <A>容器中插人元素

下面的程序 会编译出错:
#include <set>
using namespace std;
class A{};
int main(){
multiset <A> a;
a.insert( A() ); //编译出错,因为不能用“<”运算符比较两个A对象
}

5、成员函数

  iterator find (const T & val); 在容器中查找值为 val 的元素,返回其迭代器。如果找不到,返 回 end()
  iterator insert( const T & val); 将 val 插入容器中并返回其迭代器
  void insert(iterator first, iterator last); 将区间 [first, last) 中的元素插人容器
  int count( const T & val); 统计有多少个元素的值和 val 相等
  iterator lower_bound( const T & val); 查找一个最大的位置 it,使得 [begin(), it) 中所有的元素者比 val 小
  iterator upper_bound( const T & val); 查找一个最小的位置 it,使得 [it, end()) 中所有的元素都比 val 大
  pair <iterator, iterator > equal_range (const T & val); 同时求得 lower_bound 和 upper_bound
  iterator erase(iterator it); 删除 it 指向的元素,返回其后面的元素的迭代器(Visual Studio 2010 中如此,但是在 C++ 标准和 Dev C++ 中,返回值不是这样)
  iterator erase(iterator first, iterator last); 删除区间 [first, last),返回 last(Visual Studio 2010 中如此,但是在 C++ 标准和 Dev C++ 中,返回值不是这样)

  multiset 及 set 中的 find 和 count 并不是用==运算符比较元素是否和待查找的值相等的。它们进行比较的原则是:如果x比y小和y比x小同时为假,就认为 x 和 y 相等
  erase 成员函数删除一个元素后,返回下一个元素的迭代器应该是很合理的,但是 C++ 标准委员会认为,返回下一个元素的迭代器也是需要时间开销的,如果程序员不想要这个返回值,那么这个开销就是浪费的——因此在遵循 C++ 标准的 Dev C++ 中,本行无法编译通过。  

  Visual Studio 2010 将 erase 成员函数处理成返回被删元素下一个元素的迭代器。不论在哪种编译器中,用 erase 成员函数删除迭代器 i 指向的元素后,迭代器 i 即告失效, 此时不能指望 ++i 后 i 会指向被删除元素的下一个元素;相反,++i 可能立即导致出错。如果想要得到被删除元素后面那个元素的迭代器,可以在删除前获取其迭代器并保存起来(这同样适用于 set、map、multimap 的 erase 成员函数)。事实上,如果得到了某关联容器的迭代器,则该迭代器并不会因为容器中元素的插入以及其他元素的删除而失效。只要该迭代器指向的元素没有被删除,就可以一直使用它。

猜你喜欢

转载自www.cnblogs.com/Sheenagh/p/12217419.html