2016-05-08 2 views
3

Iは、本質的にコンテナであるC++クラスを持っている:C++:派生インスタンスを 'extends?'で作成します。ベースインスタンス/実装ダウンキャスト?

SimpleContainerに加え
class SimpleContainer { 
    public: 
     // Various accessor methods/iterators 
    private: 
     // The actual content 
} 

- 私はコンテンツにルールを適用するが、同じコンテンツを使用して、意味的SimpleContainerを拡張StrictContainer作成します既存のSimpleContainerインスタンス。

class StrictContainer : public SimpleContainer { 

    StrictContainer(const SimpleContainer& simple) { 
     // Check content in simple, and raise an exception 
     // if it is not valid. 
    } 
} 

- 私の問題はSimpleContainerStrictContainerの間があるべき関係の種類である:

がある-: は自然なようです。それは私が継承で示したものですが、次にSimpleContainerインスタンスの構築を拡張することに基づいてStrictContainerインスタンスを作成します - それは可能ですか?

また StrictContainerSimpleContainer部材を有する関係-ているように私はそれを実装することができ、持っているが、その後、私はStrictContainerで再びすべてのアクセサとイテレータを実装しに転送する必要があります実装はSimpleContainerです。

コンテナはかなりです。 StrictContainerを作成するときは、コンテンツを実際にコピーしません。 downcast<StrictContainer>simple入力引数の内容を確認するためにStrictContainer上のメソッドを呼び出すと

SimpleContainer simple; 
// Fill simpleContainer 

{ 
    StrictContainer* strict = downcast<StrictContainer>(simple) 
    ... 
} 

:私は自分のダウンキャスト演算子を実装するための方法だった私が望む何を思いますか?

+0

デバッグのために 'StrictContainer'を使用する場合、または' operator [] 'や' at 'のような関係をベクトルで使用する場合は、 – fghj

+0

は、なぜあなたはクラス 'StrictContainer'が必要なのでしょうか?あなたの説明から、それは 'SimpleContainer'のバリデータとしてのみ機能するようです。私には簡単な関数呼び出しとして聞こえます。 – 4386427

+0

ほとんどのコンテナは移動可能ですので、 'SimpleContainer'を' StrictContainer'に移動してコピーを避けることができます。 –

答えて

0

をだから、あなたの要件を修正再表示する必要があります。

  • StrictContainerは追加でSimpleContainer、 と同じインタフェースを持っています。 (これは、が- 界面に関係を意味する。)新しいStrictContainerSimpleContainerコンテンツをコピー を必要としないはずIntstantiating
  • 。 (これは、ハンドルを使用して分解することを意味する。)

SimpleContainerの寿命がStrictContainerにまたがると仮定すると、これはdecorator patternのように聞こえます。ですから、少し分解しても構いません - このようなインタフェースを定義するのはどうですか?

class MyInterface { 
    // No contained state needed at all. 
public: 
    virtual ~MyInterface() {} 
    virtual bool getThing1() const = 0; 
    virtual int getThing2() const = 0; 
    // and so on... 
}; 

class MyDecorator : public MyInterface { 
    MyInterface &m_impl; // Could use a smart pointer, if available. 
public: 
    MyDecorator(MyInterface &impl): m_impl(impl) {} 
    bool getThing1() const override { return impl.getThing1(); } 
    int getThing2() const override { return impl.getThing2(); } 
    // and so on... 
}; 

次に、あなたの具体的な実装:

class SimpleContainer : public MyInterface { 
    bool m_thing1; 
    int m_thing2; 
     ... // All the real state goes here. 
public: 
     ... // All the interface methods go here. 
}; 

その後、StrictContainerではなく、完全なコピーで、元SimpleContainerへのハンドルを保持する軽量コンテナです。すでにStrictContainerのコンストラクタを呼び出す必要があり、それはすでにあなたが必要なすべてを行いますので、

class StrictContainer : public MyDecorator { 
    // Additional state, if needed. 
public: 
    StrictContainer(SimpleContainer &base): MyDecorator(base) {} 
    // Additional methods (like your additional validation method. 
}; 

は、特別な「ダウンキャスト演算子」は必要ありません。

SimpleContainer simple(...); // Construct as normal. 
StrictContainer strict = simple; // Assuming no extra arguments needed. 

かなり簡単です。

ははい、あなたはデコレータのクラスのインタフェース一度に各メソッドの委任操作を記述する必要があります。 (あなたは、それらのいくつかを上書きする場合を除き)。しかし、あなたは再びそれらを実装することなく、StrictContainerクラスの複数のバリアントを定義することができます

2

is-aは災害になります。規則は、SimpleContainerに整数が含まれ、StrictContainerに奇数の整数のみが含まれているとします。

StrictContainer strict; 
SimpleContainer simple; 
strict.insert(1); // OK value is odd 
simple.insert(2); // OK simple doesn't check oddness. 
SimpleContainer& sc = strict; // Reference to base. 
sc.SimpleContainer::insert(2); // Uh-oh. That will use the simple container version 
           // of insert (even in the presence of virtual functions) 
           // and will insert an invalid even number into `simple`. 

あなたはがあり、

0

私は、アダプタと戦略(政策)のパターンの組み合わせを使用して、このような何かを実装することを好みます。あなたは完全にデータを保持する基盤となるコンテナから(アダプタです)StrictContainerを切り離すことができます。 std :: queueが(ベクトルのような)別のコンテナへのアダプタとしてどのように実装されるかと非常によく似ています。次に、StrictContainer-アダプタを任意の制約でパラメータ化できます。コンテナに要素を追加するたびに、制約が満たされているかどうかがチェックされます。それは、要素が根底にあるコンテナに追加されている場合は、それ以外の場合はあなたが(そのままコンテナを残したり、インスタンスの例外をスロー)することを好むものは何でも行うことができます。

template<typename T, typename Container, typename Constraint> 
class StrictContainer 
{ 
public: 
    StrictContainer(const Container& container = {}, Constraint constraint = {}) 
     : container_(container), constraint_(constraint) 
    { 
     validateAll(); 
    } 

    StrictContainer(Container&& container, Constraint constraint = {}) 
     : container_(std::move(container)), constraint_(constraint) 
    { 
     validateAll(); 
    } 

    // container interface ... 
    void push_back(const T& value) 
    { 
     if(!constraint_(value)) 
      throw WhateverException(); 
     container_.push_back(value); 
    } 

private: 
    void validateAll() 
    { 
     for(const auto& value : container_) 
     { 
      if(!constraint_(value)) 
       throw WhateverException(); 
     } 
    } 

    Container container_; 
    Constraint constraint_; 
}; 

あなたは、このようなStrictContainerインスタンス化できますStrictContainerは、データストアとしてのstd ::ベクトルを使用しています。この場合

StrictContainer<int, std::vector<int>, IsEvenConstraint<int>> myContainer; 

myContainer.push_back(2); 
myContainer.push_back(18); 
myContainer.push_back(7); // throws 

を、しかし、あなたはあなたのSimpleContainerのような(好みのコンテナ使用することができます)。制約は、あなたがそれに応じてインターフェイスを調整する必要があるかもしれませんサポートしたいとコンテナと制約のクラスに応じて、この

template<typename T> 
struct IsEvenConstraint 
{ 
    bool operator()(const T& value) 
    { 
     return value % 2 == 0; 
    } 
}; 

のように実装することができます。 Variadicテンプレートを使用すると、StrictContainerを拡張して複数の制約をサポートできます。制約にもラムダを使うことができます。第三の選択肢として

auto lessThanOne = [](float f) { return f < 1.0f; }; 
StrictContainer<float, std::vector<float>, decltype(lessThanOne)> myContainer2(std::vector<float>{}, lessThanOne); 
myContainer2.push_back(0.1f); 
myContainer2.push_back(1.7f); // throws 
2

、私は次の例のように、ポリシーベースのアプローチをお勧めしたい:

#include<vector> 
#include<cassert> 

template<typename T> 
struct NoCheck { 
    static bool isOk(T) { 
     return true; 
    } 
}; 

template<typename T> 
struct TheAnswer { 
    static bool isOk(T t) { 
     return false; 
    } 
}; 

template<> 
struct TheAnswer<int> { 
    static bool isOk(int t) { 
     return t == 42; 
    } 
}; 

template<typename T, template<typename> class C = NoCheck> 
struct SimpleContainer { 
    bool add(T t) { 
     if(C<T>::isOk(t)) { 
      vec.push_back(t); 
      return true; 
     } 

     return false; 
    } 

private: 
    std::vector<T> vec{}; 
}; 

int main() { 
    SimpleContainer<int> c1; 
    assert(c1.add(42)); 
    assert(c1.add(0)); 
    SimpleContainer<int, TheAnswer> c2; 
    assert(c2.add(42)); 
    assert(not c2.add(0)); 
} 

共通のインターフェースを持つことが必要条件である場合には(それは質問から明らかではありません)、あなたは、ファクトリメソッドと、まだポリシーに基づいて、型消去内部クラスでそれを得ることができます。

+0

私の好みの解決策。 OPがコピーせずに単純なコンテナから厳密なコンテナを作成したいので、テンプレート化された移動コンストラクタの実装を追加することができます。 – MikeMB

+0

@MikeMB私は今家にいませんが、できるだけ早く移動コンストラクタを追加します。 – skypjack

+0

うーん、あなたは何を得ているのか分かりますが、質問されている質問に答えているかどうかは分かりません。そして、単純な「SimpleContainer」の場合、これは維持する作業が多いかもしれません。 – geipel

関連する問題