本条款告诉我们,当我们在关联式容器中存放指针类型的对象时,就应该自定义一个满足我们需求的比较器类型,也即编写一个用于比较的谓词(重载operator()符号且返回值为bool类型的一个类)。
why?假设,我们有一个包含着string*指针的set,把一些动物名字push进去:
set<string*> ssp;// ssp: a set of string ptrs!
ssp.insert(new string ("Anteater"));
ssp.insert(new string("Wombat"));
ssp.insert(new string("Lemur"));
ssp.insert(new string("Penguin"));
for (auto it = ssp.begin(); it != ssp.end(); ++it) {
cout << *(*it) << endl;
}
然后打印该set集合中的内容:
for (auto it = ssp.begin(); it != ssp.end(); ++it) {
cout << (*it) << endl;
}
并且我们希望输出顺序是这样子的:
"Anteater"
"Lemur"
"Penguin"
"Wombat"
然后你压根就看不到。因为*it是一个string*类型,除非你这么做:
for (auto it = ssp.begin(); it != ssp.end(); ++it) {
cout << *(*it) << endl;// (*it) ==> *(*it)
}
你不妨拿这段代码去你的IDE上验证一下,多run几次,你就会发现根本得不到固定顺序的输出,也即这么做输出顺序的乱序的!得不到我们想要的输出顺序!
为了deal这种关联式容器中存放指针类型对象时其排列顺序不符合我们需求的case,现在我们就必须要自定义一个比较器(谓词)来do到我上述所说的这件事情!
自定义比较器(谓词)代码如下:
//自定义一个比较类型(谓词)
class myComparator{
public:
template<class T>
bool operator()(const T ps1, const T ps2)const {
//这里直接按值(value)传入即可,因为我们就是期待传入的参数是指针or表现得像是指针!
return *ps1 < *ps2;
}
};
整理一下上述代码:
#include<iostream>
#include<string>
#include<set>
using namespace std;
//自定义一个比较类型(谓词)
class myComparator
{
public:
template<class T>
bool operator()(const T ps1, const T ps2)const {//这里直接按值(value)传入即可,因为我们就是期待它是指针or表现得像是指针!
return *ps1 < *ps2;
}
};
int main(void) {
set<string*, myComparator> ssp;// ssp: set of string ptrs!
ssp.insert(new string("Anteater"));
ssp.insert(new string("Wombat"));
ssp.insert(new string("Lemur"));
ssp.insert(new string("Penguin"));
for (auto it = ssp.begin(); it != ssp.end(); ++it) {
cout << *(*it) << endl;
}
return 0;
}
运行结果:
可见,此时的输出就非常符合我们的要求了!
当然,为了避免内存泄露,我们在容器中存放指针类型的对象时,一般都使用智能指针(smart pointer)来帮我们自动管理这些指针的内存问题(这是一种非常好的代码规范)。
请看以下代码:
#include<iostream>
#include<string>// using string
#include<set>// using set
#include<memory>// using smart pointer
#include<algorithm>// using for_each(...)
using namespace std;
//自定义一个比较类型(谓词)
class myComparator
{
public:
template<class T>
bool operator()(const T ps1, const T ps2)const {//这里直接按值(value)传入即可,因为我们就是期待它是指针or表现得像是指针!
return *ps1 < *ps2;
}
};
int main(void) {
set<shared_ptr<string>, myComparator> ssp;// ssp: set of string ptrs!
// using make_shared<T>() to make an object(smart pointer pointed to) is a very good choice!
ssp.insert(make_shared<string>("Anteater"));
ssp.insert(make_shared<string>("Wombat"));
ssp.insert(make_shared<string>("Lemur"));
ssp.insert(make_shared<string>("Penguin"));
for_each(ssp.begin(), ssp.end(), [](const shared_ptr<string>& str) {
cout << *str << endl;// 为了obey Effective STL Term43:算法调用优先于手写的循环,这里使用for_each来遍历容器
});
cout << endl;
return 0;
}
运行结果:
可见,结果是完美符合要求的!
注意(非常重要的一个点):当STL容器与智能指针配合使用时,优先选择shared_ptr<T>而不是unique_ptr<T>。因为你存入STL容器中的all对象极有可能在后续的coding中需要被使用(这里的使用指的是拷贝copy等的操作),那么如果使用unique_ptr<T>的话,就没法do到这一点!这种独享式的指针所指向的内存只有该指针能够指向!别的指针不能指向!因为unique_ptr<T>的源码中就有如下2条定义:
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
你若不信这一经验之谈,则不妨把我上述的shared_ptr的代码换为unique_ptr,相信你也会得到如下看起来令人抓狂的Error提示:
重申一遍:本条款告诉我们,当在关联式容器(set/multiset,map/multimap)中存放指针类型的对象时,就应该自定义一个满足我们需求的比较器类型,也即编写一个用于比较的谓词(重载operator()符号且返回值为bool类型的一个类/结构体)。