2016-09-03 5 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 (새 M (새 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

과 작동하도록 인스턴스를 전달하지 않으면 개체에 기능을 추가 할 수 없습니다. 매우 흥미 롭습니다. 나는 그것을 철저히 조사하고 그것을 구현하려고 노력할 것이다. 한 노트 만 :'class I'는 인터페이스이고, 모든 공통 기능을 가진 Base 클래스는'class A '입니다. 귀하의 예에서 "A"는 사라졌습니다. 왜? 데코레이터 패턴에 중요하지 않은가? 그리고 솔직히 말해서, 내 수업 "D"도 사라졌습니다 :) 당신은 정교하게 만들 수 있습니까? (또는 상세한 예제/튜토리얼을 가르쳐 주시겠습니까?) – nass

+0

@nass :별로 중요하지 않습니다. 위와 같이'A'를 만들고'T3 >>> '할 수 있습니다. 도움이되기를 바랍니다. – linuxfever

+0

감사합니다. 당신이 틀렸을지라도 저를 바로 잡으십시오. 그러나 당신의 예를 더 많이 이해하려고 노력할수록, 당신의 클래스 "I"는 제 클래스 "A"(즉, 공통 기능이 상주하는 클래스)라고 생각하게됩니다. 제 말은, 당신의 클래스 "I"가 인터페이스가 아니라 일반적인 기능 기본 클래스 인 것처럼 보입니다. 아니? – nass