2

私はこの問題を何度も抱えています...そしてまだ満足のいく答えがありません...

特に、クラスをコンテナに入れると、後で特定の処理中にコンテナ内のすべての要素についてより多くの情報を記録する必要がありますが、処理後は余分な情報はもう必要ありません....

一部のライブラリは、データ構造に void* を定義してユーザー定義のデータ構造拡張を提供することで、上記の状況を解決しようとすることがよくあります。このQ&Aで説明されているのとまったく同じです。しかし、それはメモリ/リソース処理の問題を引き起こします...そして、このアプローチはエラーが発生しやすいと私が感じる他の問題。

オブジェクト指向プログラミングの現代では、継承とポリモーフィズムの使用を考えています。コンテナーで基本クラスのポインターを使用しますが、派生クラスのアクセサーを基本クラスに追加する必要があります。それはちょっと奇妙です...

C++ でコンテナーの比較可能性を維持しながら、クラスのプロパティを拡張する他のより良い方法はありますか?

4

6 に答える 6

3

オブジェクト自体の整合性を実際に損なうことなく、オブジェクトに関する余分なデータを格納する最良の方法は、代わりにコンテナーにデータのペアを格納することです。

struct User { ... };
struct ExtraData { ... };
typedef std::pair<User, ExtraData> UserAndExtraData;

これで、どちらの型の独立性も損なうことなく、両方の情報を一緒に格納するコンテナー型を C++ で作成できるようになりました。

std::vector<UserAndExtraData> vector;
于 2012-04-07T14:10:48.947 に答える
2

Decorator Patternを調べます。オブジェクトを処理しながら装飾し、装飾されたオブジェクトを捨てることができます。共有データが多い場合は、FlyWeight パターンを調べることもできます。

于 2012-04-07T14:10:05.860 に答える
1

「ユーザー」は、テンプレート パラメーターによって拡張できます。例えば、

template <typename... Extra>
struct User : Extra...
{
    ...
};

struct ExtraData {...};
struct ExtraExtraData {...};

using ExtraUser = User<ExtraData>;
using MoreExtraUser = User<ExtraData, ExtraExtraData>;
于 2019-06-07T21:02:46.107 に答える
0

オブジェクト指向プログラミングの現代では、継承とポリモーフィズムの使用を考えています。コンテナーで基本クラスのポインターを使用しますが、派生クラスのアクセサーを基本クラスに追加する必要があります。スタンジのようなものです...

継承を使用する場合、基本クラスに派生クラスへのポインターを配置する必要はありません。派生クラスにキャストするだけです。問題は、データがベースオブジェクトに格納されているときに派生オブジェクトにデータを取得することです.コレクションがそれらをベースタイプとして保持している場合でも、それらが派生タイプとして作成された場合にのみキャストできます。(それらが派生型として作成されている場合は、キャストするだけです!)

したがって、BaseC のコレクションがある場合は、BaseC を受け取るコピー コンストラクターを持つ新しいクラス DerivedC を作成できます。BaseC オブジェクトをそこにコピーし、DerivedC オブジェクトで処理を実行してから、これらを BaseC オブジェクトにコピーして保存することができます。Flyweight パターンを使用します。BaseC オブジェクトのコレクションがある場合、すべてのデータ メンバーを保持するストレージがないため、DerivedC クラスのふりをすることはできません。新しい DerivedC オブジェクトを作成する必要があります。

または、基本クラス オブジェクトへの (スマート ポインター) 参照を含む処理専用の新しいクラスを作成し、参照をコピーして処理を実行し、完了したら処理オブジェクトを削除します。

于 2012-04-07T14:33:57.643 に答える
0

オブジェクトがベクトル内にある場合、簡単な方法は平行ベクトルを作成することです。

void doSomething(const vector<MyObject>& my_objects)
{
  vector<ExtraData> extra_data;
  int n_objects = extra_data.size();
  extra_data.reserve(n_objects);
  for (int i=0; i!=n_objects; ++i) {
    extra_data.push_back(calcExtraData(my_objects[i]));
  }
  // now use my_objects[i] and extra_data[i] together.
  // extra data goes away when the function returns.
}

元のオブジェクトを変更する必要はなく、非常に効率的です。

他のコンテナ タイプがある場合は、マップを使用できます。

void doSomething(const set<MyObject>& my_objects)
{
  map<MyObject*,ExtraData> extra_data;
  set<MyObject>::const_iterator i=my_objects.begin(), end=my_objects.end();
  for (;i!=end;++i) {
    extra_data[&*i] = calcExtraData(*i);
  }
  // now use extra_data[&obj] to access the extra data for obj.
  // extra data goes away when the function returns.
}

これはベクトルほど効率的ではありませんが、元のクラスを変更する必要はありません。

ただし、基になるコンテナーが処理中に変更される可能性がある場合、並列構造を維持することはより困難になります。

于 2012-04-07T14:23:44.533 に答える
-1

簡単なオプションの 1 つは、「追加データ」を表す型パラメーターを追加することです...

template<class ExtraDataType>
struct MyExtensibleContainer
{
    ...
    ExtraDataType extra;
};

おそらく、このソリューションが十分でない理由を示すと、真の要件が実現するでしょう。

int および void* の例:

struct IntOrVoid
{
};

struct IntOrVoid1 : IntOrVoid
{
    int x;
};

struct IntOrVoid2 : IntOrVoid
{
    void* x;
};

typedef shared_ptr<IntOrVoid> PIntOrVoid;

then use MyExtensibleContainer<PIntOrVoid>

または代わりに:

union IntOrVoid
{
    int x_int;
    void* x_voidp;
};

then use MyExtensibleContainer<IntOrVoid>

あなたが説明している問題は、「余分な」データ型を追加することとは関係ありません。あなたが説明している問題は、多くの異質な型の 1 つを持つことができるバリアント型を保持することに関係しています。これを行うには多くの方法がありますが、これはより一般的な問題です。

于 2012-04-07T14:21:11.697 に答える