【问题标题】:c++ dynamic_cast over decorator instantiations failsc ++ dynamic_cast over decorator实例化失败
【发布时间】:2017-01-11 17:32:45
【问题描述】:

我正在尝试了解装饰器模式的工作原理以及我可以将它“拉伸”到多少以满足我的需要。在this 示例之后,我扩展了 XYZ 类。存在派生类“KLM”(来自 XYZ)

具体来说,即使我有一个装饰器模式,派生的装饰器类“KLM”也有一些功能不会出现在它们的任何基类“XYZ”、“D”、“I”或“A”中。

所以虽然通常我会将一个对象实例化为

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

这将不允许我访问 K::doVirtR() 、 L::doVirtS() 和 M::doVirtT() 函数(参见下面的代码)。要访问这些,我需要使用 dynamic_cast 将 inKLM 指针向下转换为每个“KLM”类。

问题是我只为上面表达式中最左边的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;
}

【问题讨论】:

  • 为什么您的构造函数将 ptrs 带入基类?例如,您的示例中的 I * inKLM = new L( new M( new K( new A ))); 似乎尽可能地泄漏。
  • @BenjaminBannier 据我了解,这是装饰器模式的核心。除非您将实例传递给它以供使用,否则您无法向对象添加功能

标签: c++ dynamic casting decorator


【解决方案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 是阅读更多内容的链接。

【讨论】:

  • 这很有趣。明天我会彻底研究它并尝试实施它。只需注意:class I 是接口,而我的具有所有通用功能的基类是class A。在您的示例中,“A”消失了。为什么?装饰器模式不是很重要吗?坦率地说,我的“D”课也不见了:)你能详细说明一下(或指向一个详细的例子/教程吗?)
  • @nass:没关系。你可以像我上面展示的那样创建A,然后执行`T3>>>'...希望它有所帮助。
  • 谢谢。如果我错了也请纠正我,但是我越是尝试理解您的示例,我就越认为您的类“I”是我的类“A”(即通用功能所在的类)。我的意思是,看起来您的类“I”不是接口,而是通用功能基类。没有?
  • 您好,如果您在附近,我也想问一下,当基类中的方法运行时,如何触发装饰器类中某些方法的执行。
  • 这是个好主意,但我不确定是否可以将装饰器中的 et() 函数注册到基类,以便在执行基类方法时调用它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-03
  • 1970-01-01
  • 1970-01-01
  • 2021-02-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多