2012-01-04 10 views
5

C++で、後で取得する必要のある異なるデータ型メンバーを持つ派生クラス型を受け入れる最も洗練された方法を教えてください。下の例は、Containerクラスが、BaseItemの派生した変形の一種になるアイテムを「投稿」するメソッドを提供するところを示しています。後で私は、派生したItemを元に戻し、その値を抽出したいと思います。派生クラスがC++で異なるデータ型を運ぶ最も良い方法

私が欲しい主なことは、異なる "Item"派生型を定義して渡すことができるようにしながら、Containerインターフェイス(投稿と受信)が同じままであることです。テンプレートは何とかこれでいいですか?私はむしろRTTIを使用したくないでしょう。たぶん、これにはシンプルでエレガントな答えがあるかもしれませんが、今、私はそれを考えるのに苦労しています。

class ItemBase { 
    // common methods 
}; 

class ItemInt : public ItemBase 
{ 
    private: 
    int dat; 
    public: 
    int get() { return dat; } 
}; 

class ItemDouble : public ItemBase 
{ 
    private: 
    double dat; 
    public: 
    double get() { return dat; } 
}; 

class Container { 
public: 
    void post(int postHandle, ItemBase *e);  
    ItemBase* receive(int handle); // Returns the associated Item 
}; 

int main() 
{ 
    ItemInt *ii = new IntItem(5); 
    Container c; 
    c.post(1, ii); 

    ItemInt *jj = c.receive(1); 
    int val = jj->get(); // want the value 5 out of the IntItem 
} 
+1

Boost.anyを試してみましょう。 –

+0

投稿されたコードには、各オブジェクトの 'get'が異なる戻り型を持つという問題があります。同じ種類にすることができない場合は、ストリングストリームなどを返す必要があります。 –

+0

はい、同意しますが、get()関数は仮想関数ではありません。それぞれの派生クラスが「追加」するのは「別個の機能」です。継承を "必要な"ものにする他の一般的な機能があります –

答えて

5

これは間違いなく、汎用プログラミングの候補であり、継承ではありません。ジェネリックス(テンプレート)は、異なるデータ型に対して同じ処理をしたい場合に理想的です。 ItemIntクラスとItemDoubleクラスは、オブジェクト指向設計原則に違反します(get()メソッドは、実際のサブタイプの種類に応じて異なるデータ型を返します)。そのためには一般的なプログラミングが組み込まれています。他の唯一の答えは、タグ付きのデータ型であり、私は個人的にペストのようなものを避けます。

+0

テンプレートを作成してget()を返すことに同意します。しかし、post()の第2引数はどのような型ですか?どのテンプレートパラメータでもItemを受け入れるようにしたい、T. –

+0

@innocent_bystander:一般に 'Container :: post'と' Container :: recieve'の両方のテンプレートを作成し、 'ItemBase' altogeatherをスキップします。 –

0

あなたのContainerクラスはstd :: mapのように疑わしく見えます。あなたのItemBaseクラスが、void *と大きく違うわけではないと思うユニバーサルベースクラスである "Object"の別の名前であるように思えます。私は、単一のコンテナに異なるタイプのアイテムを格納しようとするのを避けるでしょう。あなたのデザインがそうすることを求めているようであれば、私はあなたのデザインを考え直すでしょう。

0

どのようにですか?

template<typename T> 
class Item 
{ 
    private: 
    T dat; 
    public: 
    T get() { return dat; } 
}; 

class Container { 
public: 
    template<typename T> 
    void post(int postHandle, Item<T> *e);  

    template<typename T> 
    Item<T>* receive(int handle); // Returns the associated Item 
}; 

int main() 
{ 
    Item<int> *ii = new Item<int>(5); 
    Container c; 
    c.post(1, ii); 

    Item<int> *jj = c.receive<int>(1); 
    int val = jj->get(); // want the value 5 out of the IntItem 
} 
0

純粋なテンプレートアプローチは、明らかにあなたのコンテナに混在したタイプがあるために機能しません。 Boostのanyのようなもので作業することもできますが、実際に復元する必要があると思います。私だと思うが、この場合のために呼び出され、実際のアイテムを保持するタイプに依存しないと仮想メソッドに加えて、テンプレートの派生クラスを暴露基底クラスである:

class Base { 
public: 
    virtual ~Base() {} 
    virtual void post() = 0; 
}; 

template <typename T> 
class Item: public Base { 
public: 
    Item(T const& value): value_(value) {} 
    void post() { std::cout << "posting " << this->value_ << "\n"; } 
private: 
    T value_; 
}; 

このアプローチは、任意の派生Itemクラスを記述する必要がありません別の値の型の場合これらの獣を簡単に作成するには、適切な作成関数を作成したいと思うかもしれません。

template <typename T> 
std::unique_ptr<Base> make_item(T const& value) { 
    return std::unique_ptr<Base>(new Item<T>(value)); 
} 

std::unique_ptr<Base>は(あなたがC++ 2011を使用していない場合は、代わりにstd::auto_ptr<T>を使用することができます)に割り当てられたオブジェクトが解放されることを確認するために返されます。この型は他のポインタ型に簡単に変換できます。 std::shared_ptr<Base>に変更してください。これは、容器に入れるのに適しています。

関連する問題