0

私が直面している問題は、カプセル化と最適なメモリ使用をどのように組み合わせるかです。

私のコードをお見せすることはできないので、広範な (願わくば) 例で説明します。

男性のデータベースが必要だとしましょう。私たちが知りたいのは、それらの人々について 2 つのことだけです。

  1. 男性の年齢 (誕生からの時間)。
  2. 彼が住んでいる町の名前。

このデータを管理する便利で自然な方法は、人間に対応するオブジェクトを作成し、それらを配列に格納することです。

class OMan1 {
  public:
    OMan( const int &age, const astring &t ): fAge(age), fTown(t) {}
    const int& age() const: { return fAge; }
    const astring& Town() const: { return fTown; }
    astring FullId() const: { return fTown+fAge; }
  private:
    int fAge;
    astring fTown;
}

OMan mans[N];

ここで、OMan は自己完結型のオブジェクトであり、すべてが適切に満たされます。

町の名前を何千回も複製し、このようにメモリと実行時間を浪費するという事実を除いて.

私たちができる改善は、町の名前と各 OMan ストアの独立した配列を、町の年齢と町の配列へのポインタのみにすることです。

class OMan2 {
  // same functionality as for OMan1
    int fAge;
    int fTownId;
    astring* fTowns;
}

オブジェクトはまだ自己完結型であり、sizeof(int) + sizeof(void*) は sizeof(astring) よりもはるかに小さく、多くの勝利を収めています。それでも sizeof(fAge) よりも 2 ~ 3 倍多く、fTowns を何十億回も繰り返します。

メモリの最適化は私にとって非常に重要です。したがって、fAge と fTownId のみを保持し、Town() や FullId() などの機能を OMan クラスから OManDataBase クラスに移動します。

class OMan3 {
  public:
    OMan( const int &age, const int &tid ): fAge(age), fTownId(tid) {}
    const int& age() const: { return fAge; }
    const int& TownId() const: { return fId; }
    // const astring& Town() const: { return fTown; }
    // astring FullId() const: { return fTown+fAge; }
  private:
    int fAge;
    int fTownId;
}

class OManDataBase {
  // constructor, destructor
    const int& age( const int& i) const: { return fMans[i].TownId()]; }
    const astring& Town( const int& i) const: { return fTown[fMans[i].TownId()]; }
    const astring& FullId( const int& i) const: { return Town(i)+age(i); }
  private:
    vector<OMan3> fMans;
    vector<astring> fTowns;
}

また、OMan3 は現在、自己完結型のオブジェクトではありません。たとえば、それがフルネームであることを知りません。つまり、1 人でデータ処理を行う必要がある場合は、OManDataBase インスタンス全体を使用する必要があります。

OBillType47 NewBillType47( const OManDataBase &db, int i ) { ... }

それ以外の

OBillType47 NewBillType47( const OMan &m ) { ... }

ここではカプセル化が破られており、コードの可読性が明らかに低下しています。(Type47 は、Oman-S で動作する多くの関数を持つことができ、それらすべてを OManDataBase クラスに含めることはできないことを強調するために付けました)。

オブジェクトを可能な限り自己完結型に保ちながら、データ複製の問題を解決する他の方法(-s)があるのだろうか?

4

2 に答える 2

0

このような問題のための特別なデザイン パターンがあります: Flyweight パターン。使ったほうがいい。

于 2013-11-19T15:06:29.943 に答える
0

次のようなジェネリック クラスを作成できます。

enum class Towns {T1 = 0, T2 = 1...}
string TownsNames[] = {"T1", "T2"...}

class OManDB 
{
   map<Towns, OMan*> m;
// OR
   map<int, Towns> m; // map by Oman ID and town

public:
   void addOMan();
   Oman getOManById(int id);
   OMan *getOManArrByTown(Towns town);
   OMan getOManTown(int omanId);
   ...
}

アイデアは、すべてのレコードを保持し、DB のようにすべての操作を行うクラスを作成することです。ここでは、データセットと一連の操作が互いに分離されていません。すべてが 1 か所にあり、必要に応じて操作を定義します。

このようにして、内部表現を好きなように変更できます。OMan の配列を返すことができます。基本的に、既存のコードを壊すことなく、やりたいことが何でもできます。ユーザーは、OMan を取得できることを知っており、OMan のフィールドを格納する正確な方法を知る必要はありません。それらを単一の 64 ビット フィールドに格納できます (たとえば、ID が 32 ビットのみで、町が追加の 32 ビット フィールドである場合)。それはあなたに自由を与えます。

アップデート

よし、とりあえずカプセル化は脇に置いて、パフォーマンスに取り組もう。理想的には、メモリのすべてのビットに対して、ID と町の ID を 2 つの異なるベクトルに格納できます。これは、2 つのフィールドのサイズが異なり、コンパイラがパディングを作成する必要がある場合に重要な利点をもたらします。2 つの異なる配列を使用すると、データを保存し、パディングを回避するための最良のオプションが得られます (もちろん pack を使用できますが、お勧めできません)。

于 2013-11-02T08:41:49.910 に答える