set中如何存储自定义对象?
- 2020 年 3 月 11 日
- 笔记
如何在set中存储自定义对象?
set是什么
假设你已经在C++中使用过set,那么你应该知道,set中存储的元素是去重的。比如:
//来源:公众号:编程珠玑 //作者守望先生 #include <iostream> #include <set> using namespace std; int main() { set<int> a; a.insert(123); a.insert(111); a.insert(111); a.insert(2); //遍历set for(auto &it:a) { cout<<it<<endl; } return 0; }
输出结果:
2 111 123
虽然插入了两次111,但是最终只会存储一个,也就是最后set中只有三个元素。
如何在set中存储自定义对象
有时候,我们可能想通过set做一下去重的事情,对于基本数据类型,set都能很好地处理。我们看看对于自定义的对象,它的结果如何呢?
//来源:公众号【编程珠玑】 //作者:守望先生 #include <iostream> #include <set> using namespace std; class MyObject { //方便起见,暂时设置为public public: int id; string data; public: MyObject(int i,string d):id(i),data(d){} }; int main() { set<MyObject> a; a.insert(MyObject(123,"123")); a.insert(MyObject(111,"111")); a.insert(MyObject(111,"111")); a.insert(MyObject(2,"2")); for(auto &it:a) { cout<<it.id<<endl; } return 0; }
很不幸,还没来得及运行,先报错了。
error: no match for ‘operator<’ (operand types are ‘const MyObject’ and ‘const MyObject’)
从报错信息我们可以推断出,它是需要调用‘operator<’,也就是说,我们需要重载操作符<,让它可以用来判断元素是否重复。那么重载它有什么原则呢? 关于操作符的重载,可以参考《什么是运算符的重载?》。
重载原则
注意,这里是仅仅介绍去重时的原则,这里暂时未涉及排序。准则细看有很多,这里总结起来就是: 两个元素如果没有任何一个小于另外一个,则他们视为重复。 假设比较函数是f(x,y),那么当f(x,y)和f(y,x)都返回false的时候,认为他们是重复的。
调用原则
其实,set容器在判定已有元素a和新插入元素b是否相等时,是这么做的:
- 将x作为左操作数,y作为右操作数,调用比较函数,并返回比较值
- 将x作为左操作数,y作为右操作数,再调用一次比较函数,并返回比较值。 如果他们两个都返回false,则认为重复,重复的元素不会被插入到容器中。
当然需要注意的是,如果x<y为true,那么x>y应为false,所以这里应该避免两个都返回true,否则将会出现未知行为。
参考实现
对于我们前面的例子来说,假设id重复,则认为对象是相同的,那么重载的<参考实现如下:
bool operator<(const MyObject &a) const { if(this->id == a.id) { return false; } else { return this->id > a.id; } }
添加之后,重新运行,就符合预期,可以对自定义对象去重啦!
总结
对于自定义对象存储在set中,如果我们希望它按照我们指定的规则去重,就可能需要重载operator<了,那么是不是只有这一种方法呢?