2011-07-26 141 views
0

我接到了一个库,实现在抽象类组成和继承设计问题

class A{ 
    public : 
    virtual void foo() = 0 ; 
}; 


class B{ 
    public: 
     void setA(A * a) { m_a = a ;} 

     virtual void f() = 0; 
     void g() {m_a->foo();} 
    protected: 
     A * m_a ; 
}; 

要使用你只需要派生类和实现纯虚函数库的形式的算法,这些虚拟课堂,如foo(),并提供其他具体实施方法(bar())。

class dA : public A { 
    public : 
     void foo() {/* ... */} 
     void bar() {/* ... */} 
}; 

class dB : public B { 
    public : 
      void f() ; 
}; 

我通常会通过调用

dB * mydB = new dB() ; 
mydB->setA(new dA()); 
mydB->f() ; 
mydB->g() ; 

使用这些类,但我有一个设计问题,实现dB::f()的时候,因为我需要调用dA::bar(),专用于dB。但在课堂上,我只保留一个B *的参考。于是我想到了两个选项:

  • 使用dynamic_cast每次f()被称为投B::m_adB*
  • 添加m_dA成员分贝存储相同指针M_A,但可以用来访问dB特定功能。

当然,我不能改变基类。

我想知道是否有更优雅的解决方案来解决这个问题(就像我没有想到的设计模式)。如果不是,我应该选择哪一个?

+0

如果'bar()'被调用了很多,我只想用你列出的第二个选项去。 – Praetorian

+0

访问者模式(http://en.wikipedia.org/wiki/Visitor_pattern)可能对您有用。用这种对问题的抽象描述很难给出具体的建议。 –

+0

公共继承在这里可能不是正确的解决方案......'dB'对'B'中不存在的使用加以限制,因此'dB'的实例不能安全地视为'B '。 –

回答

2

你有第三个解决方案。在dB中添加函数setA()。当然,这个功能将隐藏B::setA()如果dB::setA()被Implemeted一个为这是对你有好处:

class dB : public B 
{ 
    dA *m_dA ; //add this member also! 
    public : 
      void f() 
      { 
      m_dA->bar(); //fast forward : no cast here! 
      } 
      void setA(A *a) //this hides B::setA() 
      { 
      m_dA= dynamic_cast<dA*>(a); //just one time dynamic cast! 
      if (m_dA == 0) 
      { 
       throw std::runtime_error("invalid argument"); 
      } 
      B::setA(a); //now call the hidden function in the base! 
      } 
}; 

这样,你不需要每次调用dB::f()时间,这使得呼叫快速的dynamic_cast!

+0

但是,如果你有一个'B * b = new dB;'并且调用'b-> setA(new dA)',错误的函数将被调用,并且'db :: m_dA'不会被正确更新。 –

+0

@Dennis:在这种情况下,你必须调用函数dynamic_cast (b) - > setA(new dA);' – Nawaz

+0

@ Dennis:是的,我意识到了,编辑了我的评论,再次阅读。 – Nawaz

0

你的dB只有在它实际上是dA的情况下才能工作。你需要确保情况总是如此。

因此,重写setA方法以使用dynamic_cast检查它是否真的是dA。现在无论你在那一刻保存m_dA中的结果还是后来再次进行动态转换都不重要。有一个不正确的初始化dB的可能性较小,其动态转换后可能会失败。

1

dB作为一个具体的类不应该调用dA方法一个不相关的具体类。即使使用reinterpret_cast这也是糟糕的设计,它将不相关的对象连接起来。通用的功能应该放在一个通用的基类或接口中。

例如,类A可以被认为是一个接口,因为它只有纯粹的虚拟方法。因此,如果你想在dB上使用这个接口,那么使用多继承也是安全的。在那种情况下,你当然必须实施你自己的foo

class dB : public B, public A { 
    public : 
      void f(); 
      void foo(); 
}; 

如果bar()是你想要的,由于某种原因,你可以改变界面A然后做出自己的接口,它提供了一个纯虚函数bar(),然后从你的新界面使双方dAdB继承,并实现bar(),并使用接口指针。

如果您必须使用其中一个,那么组合是要走的路,但不是可能会动态演员阵容失效的模糊基指针。或许在dB内制作一个具体的dA会员。