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

rba*_*dar 2 c++ hash qt key qhash

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

我正在使用Qt 5.7.假设我有以下内容QHash:

QHash<HashKey, HashValue> hm;
Run Code Online (Sandbox Code Playgroud)

enum HashKey {
    K1,
    K2,
    K3,
    K4,
    K5
}
Run Code Online (Sandbox Code Playgroud)

class HashValue {
    public:
        int x;
        HashValue(int x) {
            this->x = x;
        }
}
Run Code Online (Sandbox Code Playgroud)

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

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));
Run Code Online (Sandbox Code Playgroud)

我通过电话测试了它

cout << hm.value(K4).x << endl;
cout << hm.find(K4).value().x << endl;
Run Code Online (Sandbox Code Playgroud)

两者都返回相同的结果3.现在我尝试使用一个不是哈希映射的一部分的键来做同样的操作,方法是将一个整数转换为HashKey并调用上面两个方法:

cout << hm.value(static_cast<HashKey>(100)).x << endl;
cout << hm.find(static_cast<HashKey>(100)).value().x << endl;
Run Code Online (Sandbox Code Playgroud)

我得到的是8(第一次打电话value().x)和5(第二次打电话find(...).value().x)

文档说明了这一点

如果散列中没有指定键的项,则这些函数返回默认构造的值.

我按照链接default-constructed value获得了以下内容:

[...]例如,QVector使用默认构造的值自动初始化其项目,如果指定的键不在地图中,QMap :: value()将返回默认构造的值.对于大多数值类型,这只是意味着使用默认构造函数创建值(例如,QString的空字符串).但是对于像int和double这样的原始类型,以及指针类型,C++语言没有指定任何初始化; 在这些情况下,Qt的容器会自动将值初始化为0.

在我的情况下,这将意味着一个HashValue()电话.然而,我得到不同结果的事实令人困惑,至少可以说.虽然文档没有提到find(...)无效键作为参数传递时会发生什么,但我希望得到相同的结果.它说它找到了第一次出现的那个键并返回一个迭代器(显然,因为我value()在上面的调用中调用了它).

随后是上面引用的文档片段(再次回到文档中QHash)

如果要检查哈希是否包含特定键,请使用contains()

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

cout << (hm.contains(static_cast<HashKey>(100)) ? "Key 100 found" : "Key 100 not found") << endl;
Run Code Online (Sandbox Code Playgroud)

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

这里的问题是为什么所有这一切都发生了,实际上发生了什么呢?

这是项目及其代码:

HashTest.pro

QT += core
QT += gui

CONFIG += c++11

TARGET = HashTest
CONFIG += console
CONFIG -= app_bundle

TEMPLATE = app

SOURCES += main.cpp
Run Code Online (Sandbox Code Playgroud)

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();
}
Run Code Online (Sandbox Code Playgroud)

Hay*_*ayt 7

value()函数基本上只是用于访问值,而不是检查是否有一个值.

它返回一个值,无法指示该值是否为"无效".所以选择设计是否构建一个.Qt可以作为一个替代方案抛出一个异常,但由于几个原因(这与c ++标准库btw的容器相同),这里没有这样做.

其次:

你有点用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();
Run Code Online (Sandbox Code Playgroud)

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

所以当你使用

find(...).value();
Run Code Online (Sandbox Code Playgroud)

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