2016-09-03 6 views
0

デコレータパターンがどのように機能し、どのくらい私が「ストレッチ」できるかを理解しようとしています。 thisの例に続いて、私はクラスXYZを拡張しました。派生クラス "KLM"(XYZから)デコレータのインスタンス化にC++のdynamic_castが失敗する

具体的には、私はデコレータパターンを持っていますが、派生デコレータクラス "KLM"は基本クラス "XYZ"のいずれにも表示されないいくつかの機能を持っています。 「D」、「I」または「A」。通常、私は

I * inKLM = new L(new M(new K(new A))); 

などのオブジェクトをインスタンス化だろうがこれは私がK::doVirtR()、L :: doVirtS()とM :: doVirtT()関数にアクセスすることを許可しないように

(以下のコードを参照してください)。これらにアクセスするには、クラス "KLM"のそれぞれにdynamic_castを使用してinKLMポインタをダウンキャストする必要があります。

問題は、上記の式の左端newに対してのみこれを行うことができます。ダイナミックキャストのために多態性を維持する必要があることを読んだので、すべての関数で仮想デストラクタを使用しようとしました。それでも、 "外側の" new操作(この場合はクラス "L"のオブジェクト)以外のもので動作するように動的キャストを取得することはできません。

このコードをご覧ください。 dynamic_castingで "LinKLM"だけでなく、 "MinKLM"と "KinKLM"を成功させるにはどうすればいいですか?

#include <iostream> 
#include <list> 

using namespace std; 

class D; //decorator base 

struct I { //interface (for both Base and DecoratorBase 
    I(){ 
     cout << "\n| I::ctor "; 
    } 
    virtual ~I(){ 
     cout << "I::dtor |" ; 
    } 
    virtual void do_it() = 0; 
    virtual void regDecorator(D* decorator) = 0; 
    virtual void train() = 0; 

    virtual void et() = 0; 

}; 

class D: public I { //DecoratorBase : has same-named fns as Base (must be exported on I) and calls upon them. 
    public: 
    D(I * inner) : m_wrappee(inner) { 
     cout << "D::ctor "; 
     regDecorator(this); 
    } 
    virtual ~D() { 
     cout << "D::dtor "; 
     delete m_wrappee; 
    } 
    void do_it() { 
     m_wrappee->do_it(); 
    } 
    virtual void et() { 
     cout << "filling in for lack of et() in derived class\n"; 
    } //almost pure virtual, just not implemented in all derived classes 

    void train(){ 
     m_wrappee->train(); 
    } 

    private: 
    void regDecorator(D* decorator){ 
     m_wrappee->regDecorator(decorator); 
    } 

    I * m_wrappee; 
}; 

class A: public I { //Base has all the basic functionality 
    public: 
    A() { 
     cout << "A::ctor " ; 
     decList.clear(); 
    } 
    ~A() { 
     cout << "A::dtor |" ; 
    } 
    void do_it() { 
     cout << 'A'; 
    } 
    void train(){ 
     et(); 
    } 
    void regDecorator(D* decorator) 
    { 
     if (decorator) { 
      cout << "reg=" << decorator << " "; 
      decList.push_back(decorator); 
     } 
     else 
      cout << "dec is null!" <<endl; 
    } 
    private: 

    void et() 
    { 
     //size_t counter=0; 
     list<D*>::iterator it; 
     for(it=decList.begin(); it != decList.end(); it++) 
     { 
      //if ((*it)->et()) 
       (*it)->et(); 
      //else 
      // cout << "couldnt et cnt=" << counter << endl; 
      //counter++; 
     } 
    } 

    std::list<D*> decList; 
}; 



class X: public D { //DerivedDecoratorX .. 
    public: 
    X(I *core): D(core){ 
     cout << "X::ctor "; 
    } 
    virtual ~X() { 
     cout << "X::dtor "; 
    } 
    void do_it() { 
     D::do_it(); 
     cout << 'X'; 
    } 
    void doX() { 
     cout << "doX" << endl; 
    } 

    protected: 
    virtual void doVirtR() = 0; 

    private: 

    void et(){ 
     cout << "X::et" <<endl; 
    } 
}; 

class K: public X { 
    public: 
    K(I * core):X(core) { 
     cout << "K::ctor " ; 
    } 
    virtual ~K() { 
     cout << "K::dtor "; 
    } 
    void doVirtR(){ 
     cout << "doVirtK" <<endl; 
    } 

}; 

class Y: public D { 
    public: 
    Y(I *core): D(core){ 
     cout << "Y::ctor "; 
     } 
    virtual ~Y() { 
     cout << "Y::dtor "; 
    } 
    /*void et(){ 
     cout << "Y::et" <<endl; 
    }*/ 
    void do_it() { 
     D::do_it(); 
     cout << 'Y'; 
    } 
    void doY() { 
     cout << "doY" << endl; 
    } 

    protected: 
    virtual void doVirtS() = 0; 

}; 

class L: public Y{ 
    public: 
    L(I * core):Y(core) { 
     cout << "L::ctor "; 
    } 
    virtual ~L() { 
     cout << "L::dtor "; 
    } 
    void doVirtS(){ 
     cout << "doVirtL" <<endl; 
    } 
}; 

class Z: public D { 
    public: 
    Z(I *core): D(core){ 
     cout << "Z::ctor "; 
     } 
    virtual ~Z() { 
     cout << "Z::dtor "; 
    } 
    void et(){ 
     cout << "Z::et" <<endl; 
    } 
    void do_it() { 
     D::do_it(); 
     cout << 'Z'; 
    } 
    void doZ() { 
     cout << "doZ" << endl; 
    } 

    virtual void doVirtT() = 0; 

}; 

class M: public Z{ 
    public: 
    M(I * core):Z(core) { //must add D(core) here explicitly because of virtual inheritance in M's base class (Z). 
     cout << "M::ctor " ; 
    } 
    virtual ~M() { 
     cout << "M::dtor "; 
    } 
    void doVirtT(){ 
     cout << "doVirtM" <<endl; 
    } 
}; 

int main(void) //testing dynamic casting 
{ 
    I * inKLM = new L(new M(new K(new A))); 
    L * LinKLM = dynamic_cast<L *>(inKLM); 
    M * MinKLM = dynamic_cast<M *>(inKLM); 
    K * KinKLM = dynamic_cast<K *>(inKLM); 
    cout << endl; 

    if (! MinKLM) cout << "null MinKLM!" << endl; 
    if (! LinKLM) cout << "null LinKLM!" << endl; 
    if (! KinKLM) cout << "null KinKLM!" << endl; 
    //KinKLM->doVirtR(); 
    //LinKLM->doVirtS(); 
    //MinKLM->doVirtT(); 
    //LinKLM->D::train(); 
    //KinKLM->do_it(); 
    //MinKLM->doZ(); 
    delete inKLM; 
    cout << endl; 
    return 0; 
} 
+0

なぜあなたのコンストラクタはptrsをベースクラスにするのですか?たとえば、あなたのサンプルから 'I * inKLM = new L(new M(new K)));'が可能な限りリークするようです。 –

+0

@BenjaminBannier私が理解しているように、これはデコレータパターンの中心です。 – nass

答えて

1

いくつかの内部クラスで固有の機能にアクセスする必要がある場合は、mixinクラスを試してみることをお勧めします。基本的な考え方は、テンプレートクラスにテンプレートパラメータを継承させることです。私は以下のクラスを単純化していますが、原則は明確である:

#include <iostream> 

// your base class 
class I { 
public: 
    virtual void do_it() {} 
}; 

// a decorator 
template <class Base> 
class T1 : public Base { 

public: 
    void do_it() { 
     std::cout << "T1" << std::endl; 
     Base::do_it(); 
    } 

    void unique_in_T1() { 
     std::cout << "Unique in T1" << std::endl; 
    } 
}; 

// another decorator 
template <class Base> 
class T2 : public Base { 

public: 
    void do_it() { 
     std::cout << "T2" << std::endl; 
     Base::do_it(); 
    } 

    void unique_in_T2() { 
     std::cout << "Unique in T2" << std::endl; 
    } 
}; 

// yet another decorator 
template <class Base> 
class T3 : public Base { 

public: 
    void do_it() { 
     std::cout << "T3" << std::endl; 
     Base::do_it(); 
    } 

    void unique_in_T3() { 
     std::cout << "Unique in T3" << std::endl; 
    } 
}; 

int main(int argc, const char * argv[]) { 
    T3<T2<T1<I>>> my_object1; 
    my_object1.do_it(); 
    my_object1.unique_in_T2(); 

    T1<T3<I>> my_object2; 
    my_object2.do_it(); 
    my_object2.unique_in_T3(); 
    return 0; 
} 

あなたのクラスDはもう必要ありません。そのクラスの主な目的は、Iのインタフェースを維持しながら、実際にジョブを実行するオブジェクトをラップすることです。 mixinクラスでは、継承に置き換えられているため、ラップは行われません。したがって、Dクラスは必要ありません。

Hereはもっと読むためのリンクです。

+0

で作業するためにインスタンスを渡さない限り、オブジェクトに機能を追加することはできません。これは非常に興味深いものです。私は明日それを調べ、それを実装しようとします。 1つのメモのみ: 'class I'はインターフェースですが、すべての共通機能を持つBaseクラスは' class A'です。あなたの例では、 "A"はなくなっています。どうして?それはデコレータのパターンにとって不可欠ではありませんか?そして、率直に言って、私のクラス「D」も消えています:)詳細を教えてください(または詳細な例/チュートリアルを参照してください) – nass

+0

@nass:それは本当に問題ではありません。上に示したように 'A'を作成し、' T3 >>> 'を実行すると助けてくれるといいですね。 – linuxfever

+0

ありがとうございます。私が間違っている場合でも私を修正してください。しかし、私があなたの例を理解しようとすればするほど、あなたのクラス "私"は私のクラス "A"(すなわち共通の機能があるクラス)だと思います。つまり、あなたのクラス「私」はインターフェイスではなく、一般的な機能の基本クラスのように見えます。いいえ? – nass

関連する問題