0

私はクラスの階層を持っている状況があります:WidgetそしてそれDoobryはから継承するオブジェクトタイプですBase(実際には2つ以上のタイプがあります)。オブジェクトの各インスタンスには、プロパティのリストがあります。すべてのオブジェクトに共通するいくつかのプロパティと、各アイテムタイプに固有のいくつかのプロパティがあります。単純な実装は次のようになります。

enum PropertyType {
  COMMON_SIZE=0,              // first section: properties common to all
  COMMON_POSITION,
  ...
  WIDGET_PROPERTY_START=100,   // ensure enough space for expansion
  WIDGET_DONGLE_SIZE,
  WIDGET_TEXT,
  ...
  DOOBRY_PROPERTY_START=200
  DOOBRY_COLOUR
  ....
};

class Base {
public:
    Base();

    std::vector<std::pair<PropertyType, string>>  properties;
};

これにより、デバッガーで意味のある名前にマップされたプロパティのリストを表示できるという1つの目的が達成されます。ただし、いくつかの欠点があります。

  • すべてのアイテムのプロパティが1つのヘッダーで定義されています(カプセル化には適していません)
  • クラスの1つにさらにプロパティを追加する場合、将来の拡張に十分なスペースを確保するために、クラスの開始位置ごとに任意の数を選択する必要があります。

私の質問は、この目的を達成する別の方法があるかどうかです。1つの考えは、格納するのに大きく、検索に時間がかかる文字列定数を使用できるということですが、名前を一意にするのが簡単で、各アイテムタイプが独自のプロパティを定義できるという利点があります。

編集:プロパティはシリアル化される必要があるため、時間の経過とともに安定している必要があります(つまり、列挙型は変更されません)。最大100万個のオブジェクトが存在する可能性がありますが、大部分は空のプロパティテーブルを持ちます(デフォルト値を使用するため)。ルックアップのパフォーマンスは挿入よりも重要であり、文字列ハッシュを実行することによるパフォーマンスへの影響はおそらく無視できます(まだ記述していないため、まだかどうかを測定することはできません)。

4

1 に答える 1

1
struct UniqueTag {
  friend TagManager;
  char const* tag;
  UniqueTag( UniqueTag const& other):tag(other.tag) {}
  UniqueTag():tag(nullptr) {}; // being able to create the null tag is useful
  bool operator<( UniqueTag const& other )const{ return tag < other.tag; }
  bool operator==( UniqueTag const& other )const{ return tag == other.tag; }
  // do other operators
private:
  UniqueTag( char const* t ):tag(t) {}
};

#include <map> // or unordered_map for performance
class TagManager {
  std::map<std::string, UniqueTag> cache;
  std::vector< std::unique_ptr< char[] > > data;
public:
  TagManager() {};
  UniqueTag GetTag( std::string s ) {
    auto range = cache.equal_range(s);
    if (range.first != range.second) {
      return range.first->second;
    }
    std::unique_ptr< char[] > str( new char[ s.size()+1 ] );
    std::copy( s.begin(), s.end(), &str[0] );
    str[s.length()] = '\0';
    UniqueTag retval( str.get() );
    data.push_back( std::move(str) );
    if(s.length()==0) {
      retval = UniqueTag(); // empty string is null tag, so we don't have both!
    }
    cache.insert( range.first, make_pair( std::move(s), retval ) );
    return retval;
  }
};

シングルTagManagerは、文字列への一意のポインターの束を維持します。ポインタ値で比較するため、高速に比較できます。文字列から一意のタグの 1 つへの変換は遅く、単一のタグ マネージャーのアンチパターンが暗示されていますが...

代替バージョンには、UniqueTagそれ自体の隣にハッシュを貼り付け、ハッシュで物事を調べることが含まれます(デバッグで2つの文字列が同じ値にハッシュされることはないと主張します-誕生日のパラドックスにより、単純にハッシュするよりもはるかに可能性が高くなります)予想)。これにより、単一のマネージャー クラスが取り除かれます (少なくともリリースでは -- デバッグでは、衝突を検出する方法があります。ハッシュが決定論的である場合、デバッグでの衝突の欠如は、リリースでの衝突がないことを意味する可能性があります)。

適切なboost::variant<enum1, enum2, enum3>ビジュアライザーといくつかの演算子のオーバーロードを使用すると、複数の独立した を使用できますenumenumまたは、どちらが有効であるかを示すプライマリを備えた es を介した自作のユニオンenumでは、その上にビジュアライザーがあり、管理をあちこちに分割できます。どちらの場合も、 の「タイプ」のインデックスをエクスポートしてからenum値をエクスポートします。enumそのため、 の順序はenum安定している必要があり、それぞれの値はenum安定している必要がありますが、魔法の整数は必要ありません。等しいかどうかを確認するには、1 つではなく 2 つの整数のチェーン比較が必要です (1 つの 64 ビット比較にハックする方が速い場合)。

于 2013-01-15T17:03:37.120 に答える