好了,现在给你一个任务。把一系列字符串依次插入到一个set中,然后按照字典序有序的输出。好吧,为了突出重点,我把这些字符串规定为“Anteater”、“Wombat”、“Lemur”、“Penguin”。
你觉得很简单,所以你这样写了。
set<string*> ssp;
ssp.insert(new string("Anteater"));
ssp.insert(new string("Wombat"));
ssp.insert(new string("Lemur"));
ssp.insert(new string("Penguin"));
for(set<string*>::iterator it=ssp.begin();it!=ssp.end();++it)
{
cout<<*it<<endl;
}
结果是:
这不奇怪,new string("XXX")返回的是指向该串首地址的地址值,所以set中存的也是这些十六进制的地址,在输出set内容的时候(cout<<*it<<endl;)自然就输出如上内容了。也许你会这样修改
cout<<**it<<endl;
这当然输出了字符串的值,但是却不一定是按照字典序输出的
首先明确一点,set中是按节点值有序插入的,在遍历的时候也是采用中序遍历按照节点值从顺序输出。但是注意,这里的节点值是字符串首地址值而不是字符串的值。所以输出不一定能得到正确的字典序。
那应该怎么办呢?
我们知道set<string*> ssp其实是set<string*,less<string*>>ssp的简写,为了完全准确,其实应该是set<string*,less<string*>,allocator<string*>>ssp的简写,这里分配器与我们无关,故忽略。
less<string*>其实就是一个仿函数,用于人为规定set的比较规则。因此我们可以这样修改:
//添加仿函数,人为定义比较规则
struct StringLess:public binary_function<const string*,const string*,bool>
{
bool operator()(const string*ps1,const string *ps2)const
{
return *ps1<*ps2;
}
};
//重写插入操作
set<string*,StringLess> ssp;
ssp.insert(new string("Anteater"));
ssp.insert(new string("Wombat"));
ssp.insert(new string("Lemur"));
ssp.insert(new string("Penguin"));
for(set<string*,StringLess>::iterator it=ssp.begin();it!=ssp.end();++it)
{
cout<<**it<<endl;
}
结果为:
解释两点:
1)为什么必须要用仿函数而不是用函数指针呢?
因为这和set的参数类型不匹配,set要求传入的是一个类型而不是一个函数。
2)为什么要继承binary_function<const string*,const string*,bool>呢?
其实不继承也能得到正确答案。继承的好处在于......等我看明白了《条款40:使仿函数类可适配》再告诉你吧~~
关联容器中的map也支持自定义仿函数比较方法,这里就一笑而过了吧~