void *ポインターのC ++の代替(テンプレートではありません)
-
03-07-2019 - |
質問
C ++について根本的な誤解があったようです:<!> lt;
ポリモーフィックコンテナソリューションが好きです。 SOに感謝します:)
したがって、比較的汎用的なコンテナ型オブジェクトを作成する必要があります。また、ビジネス関連のロジックをカプセル化することもあります。ただし、このコンテナには基本的に任意のデータを格納する必要があります-プリミティブデータ型から複雑なクラスまですべてです。
したがって、すぐにテンプレートクラスの概念にジャンプして、それで完了します。ただし、C ++ポリモーフィズムとテンプレートがうまく機能しないことに気付きました。動作しなければならない複雑なロジックがあるため、テンプレートまたはポリモーフィズムのどちらかに固執し、両方を実行してC ++と戦おうとはしません。
最後に、どちらかをしたいのであれば、ポリモーフィズムを好むでしょう。 <!> quot;このコンテナにはComparable types <!> quot;のような制約を表す方がはるかに簡単だと思います。 -a la java。
質問のトピックに連れて行ってください:最も抽象的には、<!> quot; Container <!> quot; <!> quot; push(void * data)およびpop(void * data)<!> quot;に似た純粋な仮想インターフェース(記録のために、私は実際にスタックを実装しようとはしていません。)
ただし、トップレベルのvoid *はあまり好きではありません。具体的なコンテナーで使用できるデータの種類に制約を追加するたびに署名が変わることは言うまでもありません。
要約:要素を取得するさまざまな方法を持つ比較的複雑なコンテナがあります。コンテナに入ることができる要素の制約を変更できるようにしたいと思います。要素は、複数の種類のコンテナで機能する必要があります(特定のコンテナの制約を満たす限り)。
編集:コンテナ自体がポリモーフィックである必要があることにも言及する必要があります。それが、テンプレート化されたC ++を使用したくない主な理由です。
では-Java型インターフェースへの愛情を捨てて、テンプレートを使用する必要がありますか? void *を使用してすべてを静的にキャストする必要がありますか?または、空のクラス定義<!> quot; Element <!> quot;を使用する必要があります。それは何も宣言せず、それを<!> quot; Element <!> quotのトップレベルクラスとして使用します。階層?
スタックオーバーフローが好きな理由の1つは、応答の多くが、私が考慮していなかった他のアプローチに関する興味深い洞察を提供することです。洞察とコメントを事前にありがとうございます。
解決
要素を含むルートコンテナクラスを使用できません:
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();
これにより、タイプセーフな一般的な方法でコンテナインターフェイスが提供されます。
含めることができる要素のタイプに制約を追加する場合は、次のようにします。
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
};
他のヒント
boostの標準コンテナを使用して確認できます。 :any 本当に任意のデータをコンテナに保存する場合。
boost :: ptr_container コンテナに格納できるものは何らかのベースタイプから派生する必要があり、コンテナ自体はベースタイプへの参照のみを提供できます。
簡単なことは、Container
という抽象基本クラスを定義し、格納するアイテムの種類ごとにサブクラスを作成することです。次に、任意の標準コレクションクラス(std::vector
、std::list
など)を使用して、<=>へのポインターを格納できます。ポインタを保存するため、割り当て/割り当て解除を処理する必要があることに注意してください。
ただし、このような大幅に異なるタイプのオブジェクトを格納するために単一のコレクションが必要なという事実は、アプリケーションの設計に何か問題がある可能性を示しています。この超汎用コンテナを実装する前に、ビジネスロジックを再確認することをお勧めします。
ポリモーフィズムとテンプレートは、正しく使用すれば非常にうまく機能します。
とにかく、各コンテナインスタンスに1種類のオブジェクトのみを保存することを理解しています。その場合、テンプレートを使用します。これにより、間違ったタイプのオブジェクトを誤って保存することを防ぎます。
コンテナインターフェイスの場合:デザインによっては、テンプレート化することもできます。その場合、void push(T* new_element)
のようなメソッドを使用できます。 (不明なタイプの)コンテナにオブジェクトを追加するときに、オブジェクトについて知っていることを考えてください。そもそもオブジェクトはどこから来るのでしょうか? void*
?を返す関数それが比較可能になることを知っていますか?少なくとも、すべての保存されたオブジェクトクラスがコードで定義されている場合、それらをすべて共通の祖先、たとえばStorable
から継承し、Storable*
ではなくvoid push(Storable* new_element)
を使用することができます。
オブジェクトが常に<=>などのメソッドによってコンテナに追加されることがわかっている場合、コンテナをテンプレートにすることで付加価値はありません。ただし、Storableを保存する必要があることがわかります。
まず第一に、テンプレートとポリモーフィズムは直交する概念であり、うまく機能します。次に、なぜ特定のデータ構造が必要なのですか? STLまたはブーストデータ構造(具体的にはポインターコンテナーについて) )は機能しません。
質問をすると、状況で継承を誤用しているように思われます。 <!> quot; 制約を作成することができます。 <!> quot;特にテンプレートを使用している場合は、コンテナに何が含まれるかについて。これらの制約は、コンパイラとリンカが提供するものを超える場合があります。実際には、継承を伴うそのような種類のものには扱いにくく、実行時にエラーが残る可能性が高くなります。
ポリモーフィズムを使用すると、基本的にはコンテナの基本クラスとデータ型の派生クラスが残ります。基本クラス/派生クラスは、双方向で必要な数の仮想関数を持つことができます。
もちろん、これは、派生クラスでもプリミティブデータ型をラップする必要があることを意味します。全体的なテンプレートの使用を再検討する場合、ここでテンプレートを使用します。テンプレートであるベースから派生クラスを1つ作成し、それをプリミティブデータ型(およびテンプレートで提供される以上の機能を必要としない他のデータ型)に使用します。
特に、後でテンプレートの1つをクラスに変換する必要がある場合は、テンプレート化された各型のtypedefを使用することで作業が楽になることを忘れないでください。
Boost Concept Checkもチェックしてください。ライブラリ(BCCL)。テンプレートクラス(この場合はコンテナ)のテンプレートパラメータに制約を提供するように設計されています。
そして、他の人が言ったことを繰り返し申し上げますが、ポリモーフィズムとテンプレートを混在させるのに問題はありませんでした。
Javaに似たインターフェースを放棄し、テンプレートも使用する必要はありません。 Joshの提案汎用ベーステンプレートコンテナを使用すると、コンテナとその子を多態的に渡すことができますが、さらに、含まれるアイテムとなる抽象クラスとしてインターフェイスを実装できます。提案されたように抽象IComparableクラスを作成できなかった理由はありません。そのため、次のような多態性関数を作成できます。
class Whatever
{
void MyPolymorphicMethod(Container<IComparable*> &listOfComparables);
}
このメソッドは、IComparableを実装するクラスを含むContainerの子を取ることができるため、非常に柔軟になります。