7

C++ について根本的な誤解があったようです :<

ポリモーフィック コンテナ ソリューションが気に入っています。SO、私の注意を引いてくれてありがとう:)


したがって、比較的一般的なコンテナー タイプのオブジェクトを作成する必要があります。また、ビジネス関連のロジックをカプセル化することもあります。ただし、プリミティブ データ型から複雑なクラスまで、本質的に任意のデータをこのコンテナーに格納する必要があります。

したがって、すぐにテンプレート クラスのアイデアにジャンプして、それで完了します。ただし、C++ ポリモーフィズムとテンプレートがうまく連携しないことに気付きました。作業しなければならない複雑なロジックがあるため、テンプレートまたはポリモーフィズムのいずれかに固執し、両方を実行させて C++ と戦おうとはしません。

最後に、どちらか一方を実行したいので、ポリモーフィズムを優先します。「このコンテナには比較可能な型が含まれています」のような制約を表現する方がはるかに簡単だと思います-la Java。

質問のトピックに連れて行きます: 最も抽象的なのは、「push(void* data) と pop(void* data)」に似たものを持つ「コンテナー」の純粋な仮想インターフェイスを持つことができると想像します、実際にスタックを実装しようとしているわけではありません)。

ただし、トップレベルの void* はあまり好きではありません。言うまでもなく、具体的なコンテナーが処理できるデータの型に制約を追加するたびに、署名が変更されます。

要約: 要素を取得するさまざまな方法を持つ比較的複雑なコンテナーがあります。コンテナに入る要素の制約を変更できるようにしたいと考えています。要素は、複数の種類のコンテナーで動作する必要があります (特定のコンテナーの制約を満たす限り)。

編集:コンテナ自体が多態的である必要があることにも言及する必要があります。これが、テンプレート化された C++ を使用したくない主な理由です。

では、Java 型インターフェースへの愛を捨てて、テンプレートを使用するべきでしょうか? void* を使用して、すべてを静的にキャストする必要がありますか? または、何も宣言しない空のクラス定義「Element」を使用し、それを「Element」階層の最上位クラスとして使用する必要がありますか?

私がスタック オーバーフローを気に入っている理由の 1 つは、回答の多くが、私が考えもしなかった他のアプローチに関する興味深い洞察を提供してくれることです。ですから、あなたの洞察とコメントを事前に感謝します。

4

8 に答える 8

14

本当に任意のデータをコンテナーに保存する場合は、 boost::anyの標準コンテナーの使用を検討できます。

コンテナに保存できるものはすべて、基本タイプから派生する必要があり、コンテナ自体は基本タイプへの参照のみを提供できる、 boost::ptr_containerのようなものが必要なようです。

于 2008-10-03T19:21:11.790 に答える
5

ポリモーフィズムとテンプレートは、正しく使用すれば非常にうまく連携します。

とにかく、各コンテナー インスタンスに 1 種類のオブジェクトのみを格納する必要があることは理解しています。その場合は、テンプレートを使用してください。これにより、間違ったオブジェクト タイプを誤って保存することを防ぐことができます。

コンテナ インターフェイスについては、設計によっては、テンプレート化することもでき、void push(T* new_element). オブジェクトを (未知のタイプの) コンテナーに追加するときに、そのオブジェクトについて何を知っているかを考えてみてください。そもそもオブジェクトはどこから来るのでしょうか。void*?を返す関数 それが匹敵することを知っていますか?少なくとも、格納されているすべてのオブジェクト クラスがコード内で定義されている場合、それらすべてを共通の祖先から継承させることができStorableます。Storable*void*

オブジェクトが常に のような方法でコンテナに追加されることがわかっている場合void push(Storable* new_element)、実際には、コンテナをテンプレートにすることに付加価値はありません。しかし、Storable を格納する必要があることがわかります。

于 2008-10-03T19:16:49.773 に答える
5

簡単なことは、 という抽象基本クラスを定義し、Container保存したい項目の種類ごとにサブクラス化することです。次に、任意の標準コレクション クラス ( 、 など) を使用std::vectorstd::listて、 へのポインタを格納できますContainer。ポインターを格納するため、ポインターの割り当て/割り当て解除を処理する必要があることに注意してください。

ただし、このように大きく異なるタイプのオブジェクトを格納するために単一のコレクションが必要であるという事実は、アプリケーションの設計に何か問題がある可能性があることを示しています。この超汎用コンテナーを実装する前に、ビジネス ロジックを再検討することをお勧めします。

于 2008-10-03T19:18:36.010 に答える
3

まず第一に、テンプレートとポリモーフィズムは相反する概念であり、うまく連携します。次に、なぜ特定のデータ構造が必要なのですか? STL またはブースト データ構造 (具体的にはポインター コンテナー) についてはどうですか。

あなたの質問を考えると、あなたの状況では継承を悪用しているように思えます。特にテンプレートを使用している場合は、コンテナに入れるものに「制約」を作成することができます。これらの制約は、コンパイラとリンカーが提供するものを超える可能性があります。実際には、継承を伴うそのようなことはより厄介であり、エラーは実行時に残される可能性が高くなります。

于 2008-10-03T19:21:23.133 に答える
3

要素を含むルート Container クラスを持たないことはできますか:

template <typename T>
class Container
{
public: 

   // You'll likely want to use shared_ptr<T> instead.
   virtual void push(T *element) = 0;
   virtual T *pop() = 0;
   virtual void InvokeSomeMethodOnAllItems() = 0;
};

template <typename T>
class List : public Container<T>
{
    iterator begin();
    iterator end();
public:
    virtual void push(T *element) {...}
    virtual T* pop() { ... }
    virtual void InvokeSomeMethodOnAllItems() 
    {
       for(iterator currItem = begin(); currItem != end(); ++currItem)
       {
           T* item = *currItem;
           item->SomeMethod();
       }
    }
};

これらのコンテナーは、ポリモーフィックに渡すことができます。

class Item
{
public:
   virtual void SomeMethod() = 0;
};

class ConcreteItem
{
public:
    virtual void SomeMethod() 
    {
        // Do something
    }
};  

void AddItemToContainer(Container<Item> &container, Item *item)
{
   container.push(item);
}

...

List<Item> listInstance;
AddItemToContainer(listInstance, new ConcreteItem());
listInstance.InvokeSomeMethodOnAllItems();

これにより、タイプ セーフな一般的な方法で Container インターフェイスが提供されます。

含めることができる要素のタイプに制約を追加したい場合は、次のようにすることができます。

class Item
{
public:
  virtual void SomeMethod() = 0;
  typedef int CanBeContainedInList;
};

template <typename T>
class List : public Container<T>
{
   typedef typename T::CanBeContainedInList ListGuard;
   // ... as before
};
于 2008-10-03T19:31:25.093 に答える
1

ポリモーフィズムを使用すると、基本的に、コンテナーの基本クラスとデータ型の派生クラスが残ります。基本クラス/派生クラスは、双方向で必要な数の仮想関数を持つことができます。

もちろん、これは派生クラスでもプリミティブ データ型をラップする必要があることを意味します。テンプレートの使用全体を再考する場合、ここでテンプレートを使用します。テンプレートであるベースから 1 つの派生クラスを作成し、それをプリミティブ データ型 (およびテンプレートによって提供される以上の機能を必要としない他のもの) に使用します。

テンプレート化された型ごとに typedef を使用すると、作業が楽になる可能性があることを忘れないでください。特に、後でそれらの 1 つをクラスに変換する必要がある場合はなおさらです。

于 2008-10-03T19:21:12.197 に答える
1

また、テンプレート化されたクラス (この場合はコンテナー) のテンプレート パラメーターに制約を提供するように設計された、Boost Concept Check Library (BCCL)も確認することをお勧めします。

他の人が言ったことを繰り返しますが、ポリモーフィズムとテンプレートの混合で問題が発生したことは一度もありません。かなり複雑なことをいくつか行ってきました。

于 2008-10-03T19:43:25.257 に答える
0

Java のようなインターフェースをあきらめて、テンプレートも使用する必要はありません。 Josh の一般的な基本テンプレート Container の提案により、Container とその子をポリモーフィックに渡すことができますが、さらに、インターフェイスを抽象クラスとして実装して、含まれているアイテムにすることもできます。次のように多態的な関数を持つことができるように、提案したように抽象 IComparable クラスを作成できない理由はありません。

class Whatever
{
   void MyPolymorphicMethod(Container<IComparable*> &listOfComparables);
}

このメソッドは、IComparable を実装する任意のクラスを含む Container の任意の子を取得できるようになったため、非常に柔軟になります。

于 2008-10-03T19:47:14.233 に答える