4

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

我正在使用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;

两者都返回相同的结果,即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

文档指出

如果散列中没有具有指定键的项目,这些函数将返回一个默认构造的值

我点击了链接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;

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

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

这是项目及其代码:

HashTest.pro

QT += core
QT += gui

CONFIG += c++11

TARGET = HashTest
CONFIG += console
CONFIG -= app_bundle

TEMPLATE = app

SOURCES += 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();
}
4

1 回答 1

8

value()功能基本上只是用于访问值而不是检查您是否有一个。

它返回一个值,并且无法指示该值是否“无效”。因此,如果设计是建造一个,那么选择。作为替代方案,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();

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

所以当你使用

find(...).value();

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

于 2016-10-11T08:54:41.030 回答