Qt笔记-自定义QSet,QHash的Key

文章讲述了如何为自定义对象(如Employee)提供`operator==`和`qHash`实现,以便于在QHash中正确使用这些对象作为键。关键在于确保这两个函数能唯一标识对象,例如通过组合字段。在给定的示例中,使用了名字和出生日期来创建唯一的哈希值。此外,文章还提供了一个EncounterDB结构体的例子,该结构体的id字段用于唯一标识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

官方文档已经说得很详细了。

If you want to use other types as the key, make sure that you provide operator==() and a qHash() implementation.

Example:
 #ifndef EMPLOYEE_H
 #define EMPLOYEE_H

 class Employee
 {
 public:
     Employee() {}
     Employee(const QString &name, const QDate &dateOfBirth);
     ...

 private:
     QString myName;
     QDate myDateOfBirth;
 };

 inline bool operator==(const Employee &e1, const Employee &e2)
 {
     return e1.name() == e2.name()
            && e1.dateOfBirth() == e2.dateOfBirth();
 }

 inline uint qHash(const Employee &key, uint seed)
 {
     return qHash(key.name(), seed) ^ key.dateOfBirth().day();
 }

 #endif // EMPLOYEE_H

In the example above, we've relied on Qt's global qHash(const QString &, uint) to give us a hash value for the employee's name, and XOR'ed this with the day they were born to help produce unique hashes for people with the same name.

在此我直接总结下,方便查阅。

构造2个内联函数,方便QHash去对比一个是operator == ,一个是qHash(const QString &, uint);

这里要注意2点:

①operator==:这里要注意,判断2个自定义对象是否相等,如果有唯一标识字段,比如主键,就可以直接用那个,如果没有,就在结构体中想想,拿些字段组合可以唯一标识这个结构体;

②qHash(const QString &, uint):生成hash的,同样要传入唯一标识的,上面的例子是用name生成的hash再和出生时间异或。并且const QString &, uint这个函数是Qt的全局函数(从搬砖的角度看意思就是不需要去包QHash的头文件了)。

下面是己的例子,我这个结构体是对应数据库的,id是唯一标识:

struct EncounterDB{
    int id;
    QString type;       //F = dead
    QString phase;

    friend QDebug operator << (QDebug os, EncounterDB record){

        os << "EncounterDB(" << record.id << "," << record.type << "," << record.phase << ")";
        return os;
    }
};

inline bool operator == (const AnswerDB &db1, const AnswerDB &db2){

    return db1.id == db2.id;
}

inline uint qHash(const AnswerDB &db, uint seed){

    return qHash(db.id, seed);
}

<think>好的,用户现在想了解Qt中的qHash函数。我需要先回忆一下qHash的相关知识。qHashQt中用于为类型生成哈希值的函数,常用于QHashQSet等容器。用户可能在使用这些容器时遇到了问题,或者需要自定义类型的哈希实现。 首先,我需要解释qHash的基本作用,比如它是如何支持哈希表的,返回类型是uint。然后,可能需要介绍Qt内置支持的类型的qHash,比如基本类型、QString、QByteArray等,用户可能想知道哪些类型可以直接使用而无需自己实现。 接下来,用户可能需要自定义类型的哈希函数,所以需要给出自定义qHash的方法,包括全局函数和成员函数两种方式。这里要强调必须将qHash放在同一个命名空间里,尤其是使用ADL查找的情况。可能需要示例代码来说明如何正确实现,比如一个简单的Person类,包含姓名和年龄,然后展示如何为它编写qHash函数。 另外,用户可能关心性能优化的问题,比如如何高效组合多个字段的哈希值。这里需要提到使用位运算或者QtqHashBits、qHashRange等函数。同时,需要注意哈希冲突的问题,虽然无法完全避免,但可以通过合理设计减少冲突。 用户可能还会问到qHash的注意事项,比如一致性(如果两个对象相等,它们的哈希值必须相同),以及何时需要自定义qHash,比如当类型包含指针或动态分配的内存时,可能需要自定义哈希逻辑。 可能用户在使用QHashQSet时遇到了错误,提示没有合适的qHash函数,这时候需要检查是否为目标类型正确实现了qHash,并且是否在正确的命名空间中。此外,需要提醒用户确保哈希函数的性能,避免过于复杂的计算影响程序效率。 最后,总结qHash的重要性,强调在自定义类型时正确实现qHash的必要性,以及如何利用Qt提供的工具函数来简化实现过程。同时,可能建议用户在遇到性能问题时,考虑哈希函数的质量和效率。</think>以下是对Qt框架中`qHash`函数的逐步解析: ### 1. 核心作用 `qHash`是Qt框架中用于生成哈希值的核心函数,公式表达其基本行为: $$ \text{哈希值} = qHash(key, seed) $$ - 关键应用场景:`QHash`, `QSet`等哈希容器的底层实现 - 返回类型:`uint`(32位无符号整数) ### 2. 内置支持类型 Qt已为常见类型提供默认实现: ```cpp // 基本类型 qHash(bool) qHash(int) qHash(double) // Qt核心类型 qHash(const QString&) qHash(const QByteArray&) qHash(QLatin1StringView) ``` ### 3. 自定义类型实现 #### 方法一:全局函数(推荐) ```cpp namespace MyNamespace { class Person { public: QString name; int age; }; uint qHash(const Person &p, uint seed = 0) noexcept { // 组合成员哈希值 return qHashMulti(seed, p.name, p.age); } } ``` #### 方法二:成员函数(需配合ADL) ```cpp class Book { public: uint hash(uint seed) const noexcept { return qHash(title) ^ qHash(isbn); } private: QString title; QString isbn; }; uint qHash(const Book &b, uint seed = 0) noexcept { return b.hash(seed); } ``` ### 4. 关键规则 1. **一致性要求**: $$ a == b \Rightarrow qHash(a) == qHash(b) $$ 2. **冲突率**:应尽量降低不同对象产生相同哈希值的概率 3. **性能要求**:计算速度直接影响哈希容器操作效率 ### 5. 高级工具函数 #### 组合多个字段 ```cpp // 顺序敏感的组合方式 qHashMulti(seed, field1, field2...) // 顺序不敏感的组合方式 qHashMultiCommutative(seed, field1, field2...) ``` #### 内存块哈希 ```cpp qHashBits(const void *p, size_t len, uint seed = 0) ``` #### 范围哈希 ```cpp qHashRange(begin_iterator, end_iterator, seed = 0) ``` ### 6. 典型示例 ```cpp struct GeoCoordinate { double latitude; double longitude; int altitude; }; uint qHash(const GeoCoordinate &c, uint seed = 0) noexcept { // 使用Qt提供的组合函数(顺序敏感) return qHashMulti(seed, qHash(c.latitude), qHash(c.longitude), c.altitude); } // 使用示例 QSet<GeoCoordinate> locationSet; locationSet.insert({39.9042, 116.4074, 43}); ``` ### 7. 性能优化技巧 1. **预处理关键字段**:对字符串等复杂类型预先计算哈希 2. **避免碰撞热点**:对枚举类等小范围值添加随机种子 ```cpp qHash(enumValue) ^ qHash(seed) ``` 3. **利用缓存**:对不变对象可缓存哈希值 ### 8. 调试建议 当出现`no matching function for call to 'qHash'`错误时: 1. 确认函数签名正确 2. 检查是否在相同命名空间 3. 验证是否包含必要头文件 4. 确保没有歧义的重载 ### 9. 版本变化 | Qt版本 | 重要变化 | |--------|----------| | 5.0 | 引入`seed`参数 | | 6.0 | 强制`noexcept`修饰 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT1995

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值