了解找不到密钥时 QHash 的作用

Understanding what QHash does when key not found

注意:您可以在本文末尾找到一个最小的工作示例post。

我正在使用 Qt 5.7。假设我有以下 QHash:

QHash<HashKey, HashValue> hm;

enum HashKey {
    K1,
    K2,
    K3,
    K4,
    K5
}

class HashValue {
    public:
        int x;
        HashValue(int x) {
            this->x = x;
        }
}

我已经像这样初始化了哈希映射:

hm.insert(K1, HashValue((int)K1));
hm.insert(K2, HashValue((int)K2));
hm.insert(K3, HashValue((int)K3));
hm.insert(K4, HashValue((int)K4));
hm.insert(K5, HashValue((int)K5));

我已经通过调用

进行了测试
cout << hm.value(K4).x << endl;
cout << hm.find(K4).value().x << endl;

两者return结果相同即3。现在我尝试通过将整数转换为 HashKey 并在其上调用上述两种方法来对不属于哈希映射的键执行相同操作:

cout << hm.value(static_cast<HashKey>(100)).x << endl;
cout << hm.find(static_cast<HashKey>(100)).value().x << endl;

我得到的是8(第一次调用value().x)和5(第二次调用find(...).value().x

文档指出

If there is no item with the specified key in the hash, these functions return a default-constructed value.

我按照 link default-constructed value 得到了以下信息:

[...] for example, QVector automatically initializes its items with default-constructed values, and QMap::value() returns a default-constructed value if the specified key isn't in the map. For most value types, this simply means that a value is created using the default constructor (e.g. an empty string for QString). But for primitive types like int and double, as well as for pointer types, the C++ language doesn't specify any initialization; in those cases, Qt's containers automatically initialize the value to 0.

在我的例子中,这意味着 HashValue() 调用。然而,我得到不同结果的事实至少可以说是令人费解的。尽管文档没有提及 find(...) 在将无效密钥作为参数传递时会做什么,但我希望得到相同的结果。它只是说它找到了那个键的第一次出现并且 returns 是一个迭代器(显然因为我在上面的调用中调用了 value())。

在上面引用的文档片段之后(再次回到 QHash 的文档)

If you want to check whether the hash contains a particular key, use contains()

我可以处理每次查询哈希映射时必须调用 contains() 的问题,尽管这意味着进行两次函数调用 - 首先检查密钥是否存在,然后调用 value(...) 来获取找到有效条目时的实际值。下面调用 returns "Key 100 not found":

cout << (hm.contains(static_cast<HashKey>(100)) ? "Key 100 found" : "Key 100 not found") << endl;

我希望这个检查在内部完成,但显然这不会发生(我的猜测是为了防止对这个容器的查询功能产生一些性能影响)。

这里的问题是为什么会发生这一切,以及这一切背后到底发生了什么?

这是项目及其代码:

HashTest.pro

QT += core
QT += gui

CONFIG += c++11

TARGET = HashTest
CONFIG += console
CONFIG -= app_bundle

TEMPLATE = app

SOURCES += main.cpp

main.cpp

#include <QCoreApplication>
#include <QHash>
#include <iostream>
using namespace std;

enum HashKey {
    K1 = 0,
    K2 = 1,
    K3 = 2,
    K4 = 3,
    K5 = 4
};

class HashValue {
public:
    int x;
    HashValue(int x) { this->x = x; }
    HashValue() {}
};

int main(int argc, char *argv[])
{

    QHash<HashKey, HashValue> hm;
    hm.insert(K1, HashValue((int)K1));
    hm.insert(K2, HashValue((int)K2));
    hm.insert(K3, HashValue((int)K3));
    hm.insert(K4, HashValue((int)K4));
    hm.insert(K5, HashValue((int)K5));

    cout << hm.value(K4).x << endl;
    cout << hm.value(static_cast<HashKey>(100)).x << endl;
    cout << hm.find(K4).value().x << endl;
    cout << hm.find(static_cast<HashKey>(100)).value().x << endl;
    cout << (hm.contains(static_cast<HashKey>(100)) ? "Key 100 found" : "Key 100 not found") << endl;

    return a.exec();
}

value() 函数基本上只是用于访问值而不是检查您是否有值。

它是一个 returns 值,无法表明该值是否为 "invalid"。因此,如果设计是建造一个,那么选择。 Qt 可以作为替代方案抛出异常,但由于多种原因(与 c++ 标准库的容器相同)这里没有这样做。顺便说一句。

其次:

您使用 find() 的方式有点不对。

使用 find 你可以检查键是否在列表中,如果不在,它指向散列的 end() 迭代器

QHash< Key,Value >::const_iterator valueIt = hash.find(<something>)
if(valueIt == hash.end())
{  // not found. error handling etc. 
}
Value value = valueIt.value();

这通常是 "standard" 检查密钥是否存在并在 Map/Hash/Set/... 中访问它的方法。

所以当你使用

find(...).value();

您可能会访问导致未定义行为的 end() 迭代器。