set中是使用自定义对象
很多人在 std::set
中使用自定义对象时,总是疑惑为啥不实现 operator==
而是实现 operator<
,这是因为这些同学没有搞懂,什么是相等,什么是等价,相等(equality)是以operator为基础,如果xy为真,则判定x和y相等。等价(equavalence)是以operator<为基础,如果!(x < y) && !(y < x)为真,则判定x和y等价。
因此,通常关联容器采用“等价”,而顺序容器采用“相等”。
在 C++ 中,std::set
是一个关联容器,它包含唯一对象的集合,这些对象按照某种排序规则排列。std::set
默认使用小于运算符 <
来比较元素,以确定它们在集合中的位置。这就是为什么你需要实现 operator<
而不是 operator==
的原因。
为什么需要实现 operator<
-
排序规则:
std::set
内部使用红黑树(Red-Black Tree)来存储元素,这意味着它需要一个明确的排序规则来维持树的结构。operator<
定义了这种排序规则,确保每个元素都可以被正确地放置在其所在的位置。
-
唯一性检查:
- 虽然
std::set
默认不允许重复的键(即相同的元素),但它通过比较operator<
来实现这一点。如果两个元素都不可相互小于(即a < b
和b < a
都不成立),那么它们被视为相等,因此不会插入重复的元素。
- 虽然
-
效率:
std::set
的插入和查找操作的时间复杂度为对数级 (O(log n)),这是因为它利用了红黑树的性质。为了保持这种效率,需要一个快速的方式来决定元素的相对顺序,而这正是operator<
提供的功能。
为什么不实现 operator==
虽然 operator==
用于比较两个对象是否相等,但在 std::set
的上下文中,它并不是必需的。std::set
主要关心的是元素之间的顺序关系,而不是它们的等价关系。然而,如果你确实需要检查元素是否相等,通常会在自定义类型的类中实现 operator==
,但这不是 std::set
的要求。
示例
假设我们有一个自定义类型 Person
,并且我们想要基于年龄来排序 Person
对象。那么我们需要实现 <
运算符:
#include <iostream>
#include <set>
class Person {
public:
Person(int age) : age_(age) {}
// 关联容器中采用等价
bool operator<(const Person& other) const {
return age_ < other.age_;
}
private:
int age_;
};
int main() {
std::set<Person> persons;
persons.insert(Person(25));
persons.insert(Person(30));
persons.insert(Person(20));
for (const auto& person : persons) {
std::cout << "Age: " << person.age_ << std::endl;
}
return 0;
}
在这个例子中,std::set<Person>
会根据年龄从小到大排序 Person
对象。如果没有实现 <
运算符,std::set
将无法确定正确的排序顺序。
总结
std::set
使用<
运算符来确定元素的相对顺序,这是为了维持内部排序结构(如红黑树)的性质。- 实现
operator<
是必要的,这样才能确保std::set
正确地排序和去重。 operator==
可以用于自定义类型的其他场景,但不是std::set
的必需条件。