template<
class Key,
class Compare = std::less<Key>,
class Allocator = std::allocator<Key>
> class set;
std::set是一种关联型容器,是一个排序集合,可以用来存放Key类型的对象。set集合里的对象都是唯一的,所以通常可以用set来做去重。其中排序是通过Compare这个对比函数对象来完成的,因为要排序,对于自定义类型需要告诉编译器这个类型比大小的规则。
要使用std::set需要指定一个对比函数(Compare),这个对比函数用来对比哪个元素更小,它的唯一性也是通过这个对比函数来确保的。因为标准库底层是用红黑树来实现,它的搜索,删除和插入操作具有对数复杂度。下面来看一个例子
#include <algorithm>
#include <iostream>
#include <set>
int main() {
std::set<int> set = { 3, 1, 4, 1, 5, 9, 2, 6, 5 };
std::for_each(set.cbegin(), set.cend(), [](int x) {
std::cout << x << ' ';
});
std::cout << '\n';
}
这个例子中由于set存放的类型是int类型,所以不需要用户指定对比函数,使用默认的就可以。
而对于自定义类型,需要实现一个less函数或者重载一个“<”小于操作符,例如这个例子中的自定义Dew类中包含a,b,c三个成员,比大小的规则是先对比a,如果a一样就对比b,如果b一样就对比c。
#include <iostream>
#include <set>
#include <string>
using namespace std;
class Dew
{
public:
Dew(int _a, int _b, int _c)
: a(_a), b(_b), c(_c)
{}
bool operator<(const Dew &other) const
{
if (a < other.a)
return true;
if (a == other.a && b < other.b)
return true;
return (a == other.a && b == other.b && c < other.c);
}
int a;
int b;
int c;
};
int main()
{
std::set<Dew> set;
for(int i = 0; i < 16; i++)
{
set.emplace(i,i,i);
}
for (auto item : set)
{
cout<<item.a<<" "<<item.b<<" "<<item.c<<endl;
}
}
再看另一种场景,如果自定义类型中没有实现<和less函数,那就需要实现一个这个类型的对比函数对象。例如:
#include <iostream>
#include <set>
#include <string>
using namespace std;
class Dew
{
public:
Dew(int _a, int _b, int _c)
: a(_a), b(_b), c(_c){}
int a;
int b;
int c;
};
class DewCompare
{
public:
bool operator ()(const Dew &a, const Dew &b)
{
return (a.a < b.a
|| (a.a == b.a && a.b < b.b)
|| (a.a == b.a && a.b == b.b && a.c < b.c));
};
};
int main()
{
std::set<Dew,DewCompare> set;
for(int i = 0; i < 16; i++)
{
set.emplace(i,i,i);
}
for (auto item : set)
{
cout<<item.a<<" "<<item.b<<" "<<item.c<<endl;
}
}
set中保存的是唯一对象,但是它是如何判断两个元素如何相等呢?如果两个元素都不小于对方,那么就可以判断两个元素相等:!comp(a, b) && !comp(b, a)。举个例子,假如int = 3,int b =3,那么 a < b 为false,b < a也为false,所以推出a == b。