- set
- set外部比較器
- set內部比較器
- unordered_set
- unordered_set外部比較器
- unordered_set內部比較器
- 代碼
以 Heroes 類為例進行演示 如何自定義set/unordered_set內外部比較器 ,類 Heroes定義如下:
class Heroes {
public:
Heroes(string _name, int _age) :name(_name), age(_age) {}
private:
string name;//私有變數 name
int age;//私有變數 age
};
set
現在我們需要使用 set 容器對Heroes類的一些物件進行存盤,我們知道set的底層實作是RB-Tree(紅黑樹),因此如何對這些物件進行排序就成了我們必須手動完成的事情了,
下面是set容器用到的引數串列:
template <class _Kty, class _Pr = less<_Kty>, class _Alloc = allocator<_Kty>>
其中提供了默認的排序規則less:
// STRUCT TEMPLATE less
template <class _Ty = void>
struct less {
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _FIRST_ARGUMENT_TYPE_NAME;
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _SECOND_ARGUMENT_TYPE_NAME;
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef bool _RESULT_TYPE_NAME;
constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const {
return _Left < _Right;
}
};
less中對 () 進行了多載,因此我們也可以仿照它定義比較器,
set外部比較器
class externalCmp{
public:
bool operator()(const Heroes& a, const Heroes& b) const{
return a.age > b.age;//set內部按照自定義規則排序
}
};
該比較是將Heroes物件按照年齡從大到小存盤,
外部比較器使用示例:
//使用 外部比較器 按照既定規則排序
void set_方法1() {
set<Heroes, externalCmp> hero;
hero.insert({ "劉備",30 });
hero.insert({ "關羽", 29 });
hero.insert({ "張飛", 34 });
hero.insert({ "黃忠", 62 });
hero.insert({ "馬超", 25 });
hero.insert({ "趙云", 27 });
for (auto& it : hero) {
cout << it << endl;
}
}
注意:由于Heroes類中的成員屬性都設定為了私有,這里用到自己定義的類需要在Heroes類中形成為友元關系,(或者將屬性改為public)

特別的,細心的小伙伴是不是發現輸出的格式有點意外,沒錯,這里對 << 進行了多載:
//多載 << 運算子,讓其直接輸出 Heroes 物件資訊
ostream& operator<<(ostream& O,const Heroes& os) {
O <<"英雄:" <<os.name <<" 年齡:"<< os.age<<"歲";
return O;
}
set內部比較器
在自定義類中對 < 進行多載即可,
//內部多載 < 自定義排序規則
constexpr bool operator<(const Heroes& hero)const {
return age<hero.age;
}
效果與上面的外部比較器一致,就不多加贅述了,
unordered_set
與set不同的是,unordered_set底層是以哈希表為基礎的一種容器,讓我們看看頭檔案中對它的定義:
template <class _Kty, class _Hasher = hash<_Kty>, class _Keyeq = equal_to<_Kty>, class _Alloc = allocator<_Kty>>
以上為unordered_set()的引數串列,其中后面三個均為可預設引數,默認為我們提供了 hash<_Kty>、equal_to<_Kty>(_Kty為類模板)
hash<型別> 的作用是根據傳遞的引數通過哈希函式生成下標
在頭檔案中定義分別如下:
// STRUCT TEMPLATE hash
template <class _Kty>
struct hash
: _Conditionally_enabled_hash<_Kty,
!is_const_v<_Kty> && !is_volatile_v<_Kty> && (is_enum_v<_Kty> || is_integral_v<_Kty> || is_pointer_v<_Kty>)> {
// hash functor primary template (handles enums, integrals, and pointers)
static size_t _Do_hash(const _Kty& _Keyval) noexcept {
return _Hash_representation(_Keyval);
}
};
unordered_set外部比較器
我們可以仿照其在外部定義一個類實作hash功能:
class hash_Heroes {
public:
size_t operator()(const Heroes& hero)const {
hash<string> hs;
return hs(hero.name);//哈希值由物件的name屬性決定
}
};
**equal_to<_Kty>**則定義的是如何判斷兩個物件是否相同,
同樣我們找出其在庫檔案中的定義:
// STRUCT TEMPLATE equal_to
template <class _Ty = void>
struct equal_to {
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _FIRST_ARGUMENT_TYPE_NAME;
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _SECOND_ARGUMENT_TYPE_NAME;
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef bool _RESULT_TYPE_NAME;
constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const {
return _Left == _Right;
}
};
不難發現函式中僅是對 **()**進行了多載,因此我們仿照其定義自己的判定規則:
class equal_to_Heroes {
public:
bool operator()(const Heroes& hero1, const Heroes& hero2)const {
return hero1.name == hero2.name && hero1.age== hero2.age;
}//只有當姓名與年齡均相同時破判定為相同物件
};
外部比較器使用示例:
//傳入指定的模板引數,告訴容器如何哈希一個Heroes物件,以及如何比較兩個Heroes物件是否相同
void unordered_set方法1() {
unordered_set<Heroes, hash_Heroes, equal_to_Heroes> hero3;
hero3.insert({ "劉備",30 });
hero3.insert({ "關羽", 29 });
hero3.insert({ "張飛", 34 });
hero3.insert({ "黃忠", 62 });
hero3.insert({ "馬超", 25 });
hero3.insert({ "趙云", 27 });
hero3.insert({ "趙云", 28 });
hero3.insert({ "黃忠", 70 });
for (auto& it : hero3) {
cout << it << endl;
}
}

unordered_set內部比較器
這里僅對 equal_to<> 內部進行修改:多載比較運算子 == 即可:
//內部多載 == 自定義哈希集合如何判斷物件為相同
const bool operator==(const Heroes& hero)const {
return name == hero.name && age == hero.age;
}
代碼
#include <iostream>
#include <set>
#include <unordered_set>
#include <string>
using namespace std;
class Heroes {
public:
Heroes(string _name, int _age) :name(_name), age(_age) {}
friend class externalCmp;
friend class intervalCmp;
friend class hash_Heroes;
friend class equal_to_Heroes;
//內部多載 < 自定義排序規則
constexpr bool operator<(const Heroes& hero)const {
return age > hero.age;
}
//內部多載 == 自定義哈希集合如何判斷物件為相同
const bool operator==(const Heroes& hero)const {
return name == hero.name && age == hero.age;
}
//多載輸出符,便于統一輸出
friend ostream& operator<<(ostream& O, const Heroes& os);
private:
string name;
int age;
};
class externalCmp{
public:
bool operator()(const Heroes& a, const Heroes& b) const{
return a.age > b.age;//set內部按照自定義規則排序
}
};
//使用 外部比較器 按照既定規則排序
void set_方法1() {
set<Heroes, externalCmp> hero;
hero.insert({ "劉備",30 });
hero.insert({ "關羽", 29 });
hero.insert({ "張飛", 34 });
hero.insert({ "黃忠", 62 });
hero.insert({ "馬超", 25 });
hero.insert({ "趙云", 27 });
for (auto& it : hero) {
cout << it << endl;
}
}
//改變 內部比較器 operator< 定義其排序規則
void set_方法2() {
set<Heroes> hero2;
hero2.insert({ "劉備",30 });
hero2.insert({ "關羽", 29 });
hero2.insert({ "張飛", 34 });
hero2.insert({ "黃忠", 62 });
hero2.insert({ "馬超", 25 });
hero2.insert({ "趙云", 27 });
for (auto& it : hero2) {
cout << it << endl;
}
}
class hash_Heroes {
public:
size_t operator()(const Heroes& hero)const {
hash<string> hs;
return hs(hero.name);
}
};
class equal_to_Heroes {
public:
bool operator()(const Heroes& hero1, const Heroes& hero2)const {
return hero1.name == hero2.name && hero1.age==hero2.age;
}
};
//傳入指定的模板引數,告訴容器如何哈希一個Heroes物件,以及如何比較兩個Heroes物件是否相同
void unordered_set方法1() {
unordered_set<Heroes, hash_Heroes, equal_to_Heroes> hero3;
hero3.insert({ "劉備",30 });
hero3.insert({ "關羽", 29 });
hero3.insert({ "張飛", 34 });
hero3.insert({ "黃忠", 62 });
hero3.insert({ "馬超", 25 });
hero3.insert({ "趙云", 27 });
hero3.insert({ "趙云", 28 });
hero3.insert({ "黃忠", 70 });
for (auto& it : hero3) {
cout << it << endl;
}
}
void unordered_set方法2() {
hash<Heroes>;//根據一個Heroes物件生成一個下標
equal_to<Heroes>;//比較兩個物件是否相同的標準
unordered_set<Heroes, hash_Heroes> hero4;
hero4.insert({ "劉備",30 });
hero4.insert({ "關羽", 29 });
hero4.insert({ "張飛", 34 });
hero4.insert({ "黃忠", 62 });
hero4.insert({ "馬超", 25 });
hero4.insert({ "趙云", 27 });
hero4.insert({ "趙云", 28 });
for (auto& it : hero4) {
cout << it << endl;
}
}
//多載 << 運算子,讓其直接輸出 Heroes 物件資訊
ostream& operator<<(ostream& O, const Heroes& os) {
O << "英雄:" << os.name << " 年齡:" << os.age << "歲";
return O;
}
int main() {
set_方法1();
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/263309.html
標籤:其他
下一篇:C語言之實作單鏈表的基本操作
