注意:你可以在这篇文章的末尾找到一个最小的工作示例。
我使用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()返回a如果指定的键不在映射中,则默认构造的值。为对于大多数值类型,这仅仅意味着使用默认构造函数(例如QString的空字符串)。但对于int和double等基本类型,以及指针类型c++语言没有指定任何初始化;在这些情况下,Qt's容器自动将该值初始化为0。
在我的情况下,这将意味着HashValue()
调用。然而,我得到不同结果的事实至少可以说是令人困惑的。我希望得到相同的结果,尽管文档没有提到find(...)
在传递无效键作为参数时做什么。它只说它找到该键的第一个出现并返回一个迭代器(显然,因为我在上面的调用中调用了value()
)。
上面引用的文档片段后面是
(再次回到QHash
的文档中)如果要检查散列是否包含特定键,请使用包含()
我可以处理每次查询哈希映射时必须调用contains()
,尽管这意味着进行两个函数调用-首先检查是否存在key,然后调用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
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()
函数基本上只是用于访问值而不是检查是否有一个值。
它返回一个值,没有办法表明该值是否"无效"。所以选择如果设计是建造一个。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()
迭代器,这会导致未定义的行为。