十三、关联容器的等价与比较函数的返回值
两个对象是否相等是基于operator==。因为operator==可以自定义,所以,两个对象相等,并不意味着对象所有的数据成员都相等
而关联容器对相同元素的定义是等价,默认是基于operator<。比如有个map<string, int>对象,该对象key的排序方式默认就是operator<,a与b等价关系可以如下表示:
!(a<b) && !(b<a)
上述表达式的含义是:按照一定的排序规则,a,b中的任何一个都不在另一个前面,则二者等价
如果排序规则不是operator<,则两个元素等价的条件是
!keycomp(x,y) && !keycomp(y,x)
其中,keycomp表示key的排序规则
示例
class comp
{
public:
bool operator()(const string &a, const string &b)
{
if(a.size()!=b.size()) {
return true;
}
for (unsigned i=0;i<a.size();++i) {
if (tolower(a[i])!=tolower(b[i])) {
return true;
}
}
return false;
}
};
int main(int argc, char const *argv[])
{
set<string, comp> s;
pair<set<string, comp>::iterator, bool> res1=s.insert("abc");
auto res2=s.insert("ABC");
auto res3=s.insert("AB");
cout<<res3.second<<endl;
cout<<res1.second<<","<<res2.second<<endl;
cout<<(s.find("ABC")!=s.end())<<endl;
cout<<(find(s.begin(), s.end(), "ABC")!=s.end())<<endl;
return 0;
}

通过输出结果可知,等价的判定规则生效,当字符串abc被insert被添加到容器中后,因为ABC和abc满足等价条件,为了保证set中所有的元素都是不等价的,所以,ABC没有被添加到容器中
但是又因为abc和ABC是等价的,所以s.find("ABC")!=s.end()的结果为true,说明查找成功
而标准库的find函数是基于相等来进行查找的,所以find(s.begin(), s.end(), "ABC")直接返回了尾后迭代器,没查找到ABC
上面的代码,ABC和abc都会让函数调用运算符返回false,从而满足!comp("ABC", "abc") && !comp("abc", "ABC")的表达式结果为true,进而判定二者等价,从而二者只有一个被添加到容器中,如果上述代码的第7,11行返回false,那么就会判定ABC和abc不等价,就会将ABC和abc都插入到容器中,违背了忽略大小写的实现原则
十四、让关联容器的比较规则在等值的情况下返回false。
如果关联容器的比较规则在等值的情况下返回true,就有可能使set中出现等值的元素,破坏set
示例
bool comp(int a, int b)
{
return a<=b;
}
int main(int argc, char const *argv[])
{
set<int, bool(*)(int, int)> s;
s.insert(10);
s.insert(10);
return 0;
}

原因:当插入第二个10时,会根据比较规则判定这两个10是否等价,因为!(10<=10) && !(10>=10) 的表达式的结果为false,所以判定10和10不等价(相等的值但是却不等价),所以将第二个10插入到set中,所以set中有两个重复的10,出现了重复的key,破坏了set,所以出现段错误
解决办法就是让比较规则在等值的情况下返回false,进而满足等价的条件。此外,让比较规则在等值的情况下返回false是任何一个严格弱序的函数必须遵守的规则
上述两个原则说明,关联容器的排序规则是基本等价而不是相等,两个元素是否等价的判定条件完全依赖比较函数,可以自由实现,但是可以自由实现的前提是不能突破关联容器的原则
十五、当关联容器的key的类型为指针时,要指定比较函数
如果关联容器中的元素是指针,那么关联容器默认是按照指针的16进制的地址值的大小进行升序排列,而不是根据指针指向的对象的operator<进行排列
示例
int main(int argc, char const *argv[])
{
set<string *> s;
s.insert(new string("qwer"));
s.insert(new string("1234"));
s.insert(new string("abcd"));
for (set<string *>::iterator it=s.begin();it!=s.end();++it) {
cout<<*it<<endl;
cout<<**it<<endl;
}
return 0;
}

可见上述输出地址是升序的,但是字符串完全乱序
解决办法就是重新指定比较规则
bool comp(string *ps1, string *ps2)
{
assert(ps1!=nullptr && ps2!=nullptr);
return *ps1 < *ps2;
}
int main(int argc, char const *argv[])
{
set<string *, bool(*)(string *, string *)> s(comp);
s.insert(new string("qwer"));
s.insert(new string("1234"));
s.insert(new string("abcd"));
for (set<string *>::iterator it=s.begin();it!=s.end();++it) {
cout<<*it<<endl;
cout<<**it<<endl;
}
return 0;
}

可见,重新指定比较函数后,输出结果是按照字符串的大小升序输出
参考
《Effective STL》
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

被折叠的 条评论
为什么被折叠?



