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ここで、整数をキャストして上記の2つのメソッドを呼び出すことにより、ハッシュマップの一部ではないキーで同じことを試みました。

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

私が得たのは8( を使用した最初の呼び出しの場合value().x) と5( を使用した 2 番目の呼び出しの場合find(...).value().x)です。

ドキュメントには次のように記載されています

ハッシュに指定されたキーを持つアイテムがない場合、これらの関数はデフォルトで構築された値を返します。

のリンクをたどったdefault-constructed valueところ、次のものが得られました。

[...] たとえば、QVector はそのアイテムをデフォルトで構築された値で自動的に初期化し、指定されたキーがマップにない場合、QMap::value() はデフォルトで構築された値を返します。ほとんどの値の型では、これは値がデフォルトのコンストラクタ (QString の空の文字列など) を使用して作成されることを意味します。ただし、int や double などのプリミティブ型、およびポインター型の場合、C++ 言語は初期化を指定しません。そのような場合、Qt のコンテナーは自動的に値を 0 に初期化します。

私の場合、これはHashValue()呼び出しを意味します。しかし、私が異なる結果を得たという事実は、控えめに言っても困惑しています。find(...)ドキュメントには、無効なキーが引数として渡された場合の動作については言及されていませんが、同じ結果が得られると予想されます。そのキーの最初の出現を見つけてイテレータを返すと言っているだけです(明らかに、value()上記の呼び出しでそれを呼び出しているためです)。

上から引用したドキュメント スニペットの後に (再び のドキュメントに戻るQHash)、

ハッシュに特定のキーが含まれているかどうかを確認するには、contains() を使用します。

contains()ハッシュマップをクエリするたびに呼び出す必要があることに対処できますが、これは 2 つの関数呼び出しを行うことを意味します。最初にキーが存在するかどうかを確認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();
}
4

1 に答える 1

8

このvalue()関数は基本的に、値があるかどうかをチェックするのではなく、値にアクセスするためだけのものです。

値を返しますが、値が「無効」かどうかを示す方法はありません。したがって、設計が1つを構築することであったかどうかの選択。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 に答える