21

シナリオ

平等に比較できるようにしたいクラスがあります。クラスは大きく (ビットマップ イメージが含まれています)、複数回比較するので、効率を高めるためにデータをハッシュし、ハッシュが一致する場合にのみ完全な等価性チェックを行います。さらに、オブジェクトの小さなサブセットのみを比較するため、等価性チェックが最初に実行されたときにのみハッシュを計算し、その後の呼び出しには保存された値を使用します。

class Foo
{
public:

   Foo(int data) : fooData(data), notHashed(true) {}

private:

   void calculateHash()
   {
      hash = 0; // Replace with hashing algorithm
      notHashed = false;
   }

   int getHash()
   {
      if (notHashed) calculateHash();
      return hash;
   }

   inline friend bool operator==(Foo& lhs, Foo& rhs)
   {
      if (lhs.getHash() == rhs.getHash())
      {
         return (lhs.fooData == rhs.fooData);
      }
      else return false;
   }

   int fooData;
   int hash;
   bool notHashed;
};

バックグラウンド

この回答のガイダンスによると、等値演算子の正規形は次のとおりです。

inline bool operator==(const X& lhs, const X& rhs);

さらに、演算子のオーバーロードに関する次の一般的なアドバイスが提供されます

常に、オペレーターの既知のセマンティクスに固執してください。

質問

  1. 私の関数は、ハッシュを実行するためにそのオペランドを変更できる必要があるため、それらを non- にする必要がありましたconst。これによる潜在的な悪影響はありますか (例としては、標準ライブラリ関数や、オペランドoperator==を持つことを期待するSTL コンテナーなどがあります)。const

  2. operator==ミューテーションが観察可能な効果を持たない場合 (ユーザーがハッシュの内容を見る方法がないため)、ミューテーション関数はよく知られているセマンティクスに反していると見なされるべきですか?

  3. 上記のいずれかに対する答えが「はい」の場合、より適切なアプローチは何ですか?

4

6 に答える 6

37

メンバーにとって完全に有効なユースケースのようですmutableoperator==const 参照によってパラメーターを取得し、クラスmutableにハッシュ値のメンバーを与えることができます (そしてそうすべきです) 。

クラスには、それ自体がconstメソッドとしてマークされ、初めて呼び出されたときにハッシュ値を遅延評価するハッシュ値のゲッターがあります。mutableこれは、ユーザーの観点からオブジェクトを変更しないため、実際に言語に追加された理由の良い例です。これは、コストのかかる操作の値を内部的にキャッシュするための実装の詳細にすぎません。

于 2013-04-12T15:15:20.957 に答える
12

mutableキャッシュしたいが、パブリック インターフェイスには影響しないデータに使用します。

いま、「変異」→ mutable.

次に、オブジェクトが使用するコードに何を提供することを保証するかという論理的な観点から考えます。 const

于 2013-04-12T15:14:21.330 に答える
3

比較時にオブジェクトを変更しないでください。ただし、この関数はオブジェクトを論理的に変更しません。簡単な解決策:hashハッシュの計算はキャッシュの一種であるため、変更可能にします。参照: 'mutable' キーワードには、const 関数による変数の変更を許可する以外の目的がありますか?

于 2013-04-12T15:18:21.007 に答える
0

変更可能なルートに進むことができますが、それが必要かどうかはわかりません。mutable を使用しなくても、必要に応じてローカル キャッシュを実行できます。例えば:

#include <iostream>
#include <functional> //for hash

using namespace std;

template<typename ReturnType>
class HashCompare{
public:
    ReturnType getHash()const{
        static bool isHashed = false;
        static ReturnType cachedHashValue = ReturnType();
        if(!isHashed){
            isHashed = true;
            cachedHashValue = calculate();
        }
        return cachedHashValue;
    }
protected:
    //derived class should implement this but use this.getHash()
    virtual ReturnType calculate()const = 0;
};



class ReadOnlyString: public HashCompare<size_t>{
private:
    const std::string& s;
public:
    ReadOnlyString(const char * s):s(s){};
    ReadOnlyString(const std::string& s): s(s){}

    bool equals(const ReadOnlyString& str)const{
        return getHash() == str.getHash();
    }
protected:
    size_t calculate()const{
        std::cout << "in hash calculate " << endl;
        std::hash<std::string> str_hash;
        return str_hash(this->s);
    }
};

bool operator==(const ReadOnlyString& lhs, const ReadOnlyString& rhs){ return lhs.equals(rhs); }


int main(){
    ReadOnlyString str = "test";
    ReadOnlyString str2 = "TEST";
    cout << (str == str2) << endl;
    cout << (str == str2) << endl;
}

出力:

 in hash calculate 
 1
 1

必要な場所にローカルにするのではなく、メンバー変数として isHashed を保持する必要がある理由を保持する正当な理由を教えてください。本当に必要な場合は、「静的」な使用法からさらに逃れることができることに注意してください。私たちがしなければならないことは、専用の構造/クラスを作成することだけです。

于 2013-04-12T22:06:30.587 に答える