19.理解相等(equality)和等价(equivalence)的区别。
相等的概念是以operator==为基础的,而等价的概念则是以operator<为基础的。
相等基于operator==,所以应该记住,x和y有相等的值并不意味的它们所有的数据成员都相等。
而等价关系是以“”在已排序的区间中对象值的相对顺序“为基础。如果按照关联容器c的排列顺序,每个都不在另一个的前面。假如x和y有等价的值,那么
!c.key_comp(x,y) && !c.key_comp(y,x) 为true。
20.为包含指针的关联容器指定比较类型。
如果想让指针在集合中按照某种需要的排序方式排序,那么不能使用默认的排序比较函数,必须自己编写比较子类。仔细分析下面的代码,结果都一样,但是个人还是比较喜欢StringPtrLess2。
#include <iostream>
#include <string>
#include <set>
#include <algorithm>
#include <iterator>
using namespace std;
class StringPriLess
{
public:
bool operator()(const string *ps1,const string *ps2) const
{
return *ps1 < *ps2;
}
};
class StringPtrLess2
{
public:
template<typename T>
bool operator()(const T* ps1, const T *ps2)
{
return *ps1 < *ps2;
}
};
class StringPtrLess3
{
public:
template<typename T>
bool operator()(T pt1,T pt2) const
{
return *pt1 < *pt2;
}
};
void print(const string *ps)
{
cout << *ps << endl;
}
class Dereference
{
public:
template<typename T>
const T& operator()(const T* ptr) const
{
return *ptr;
}
};
int main()
{
typedef set<string*,StringPtrLess3> stringPtrSet;
stringPtrSet ssp;
ssp.insert(new string("Anteater"));
ssp.insert(new string("Wombat"));
ssp.insert(new string("Lemur"));
ssp.insert(new string("Penguin"));
// for_each(ssp.begin(),ssp.end(),print);
transform(ssp.begin(),ssp.end(),ostream_iterator<string>(cout,"\n"),Dereference());
return 0;
}
记结论。比较函数的返回值表明的是按照该函数定义的排顺序,一个值是否在另一个之前,相等的值从来不会有前后顺序关系,所以,相等的值,比较函数总是应该返回false。
如:set<int,less_equal<int> >s就是一个典型的在等值的情况下返回true的例子。如果插入同样的值,比如说10,按照条款19,两者等价是下面式子
!c.key_comp(x,y) && !c.key_comp(y,x) 为true。
而!(10 <= 10) && !(10 <= 10)这个式子结果为false,证明不等价,两个10会插在一起(挨个),从技术上讲,这样的容器会导致不确定的行为。
而a < b是等价于b > a而不是b >= a。所以说set<int,less<int> >和set<int,greater<int> >都对,set<int,less_equal<int> >和set<int,greater_equal<int> >是错误的。
结论就是,比较函数在等值的情况下返回false,不然会破坏所有的标准关联容器。
22.切勿直接修改set或者multiset中的键。
map和multimap的键不能更改,而set和multiset可以修改与集合排序无关的部分。
而C++标准委员会认为map/multimap的键应该是const 的,而set/multiset的值应该不是。所以如果要改变set或者multiset中的元素,一定不要修改键的部分。
下面提供一种安全的修改set或者multiset的方法:
EmpIDSet se;
Employee selectedID;
……
EmpIDSet::iterator i = se.find(selectedID);
if(i != se.end())
{
Employee e(*i);
e.setTitle("Corporate Deity");
se.erase(i++);
se.insert(i,e);
}
上面的方法大致分五步:
1.找到想要修改的元素。
2.对这份元素做一个拷贝。
3.修改该拷贝。
4.把该元素从容器中删除。
5.把新的值插到容器中。
23.考虑用排序的vector代替关联容器。
关联容器不是用来排序的。
24.当效率至关重要的时候,请在map::operator[]与map::insert之间谨慎做出选择。
map的[]实际上是首先调用默认构造函数,然后立刻赋予新值。
结论就是,如果要更新一个已有的元素,优先考虑operator[],但是如果要添加一个新的元素,最好选择insert。
25.熟悉非标准的哈希容器。