我从一个以抽象类
的形式实现算法的库中获得了这些虚拟类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 *来保持对dB的引用。然后我想到了两个选择:
dynamic_cast
时使用f()
将B::m_a
投射到dB*
当然我不能改变基类。
我想知道这个问题是否有更优雅的解决方案(就像我没想到的设计模式)。如果没有,我应该选择哪一个?
答案 0 :(得分:2)
你有第三个解决方案。在setA()
中添加功能dB
。当然,如果B::setA()
被实现为:{/ p>,此功能将隐藏dB::setA()
,这对您有好处。
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,这样可以快速拨打电话!
答案 1 :(得分:1)
dB
作为具体类不应该在dA
上调用不相关的具体类的方法。即使使用reinterpret_cast
,这也是糟糕的设计,无法将不相关的物体耦合在一起。常用功能应放在公共基类或接口中。
例如,类A
可以被视为一个接口,因为它只有纯虚方法。因此,如果您想在dB
上使用该接口,那么使用多重继承是安全的。在这种情况下,您当然必须实现自己的foo
。
class dB : public B, public A {
public :
void f();
void foo();
};
如果bar()
是您想要的那个,并且由于某种原因您可以更改接口A
,然后创建自己的接口,提供纯虚函数bar()
,然后同时创建dA
dB
1}}和bar()
从您的新接口继承并相应地实现dA
并使用接口指针。
如果你必须在另一个方面使用一个,那么组合是要走的路,而不是可能使动态转换失败的模糊基指针。在dB
内建立一个具体的{{1}}成员。
答案 2 :(得分:0)
只有当它具有的A实际上是dA时,你的dB才能工作。你需要确保始终如此。
因此,覆盖setA方法以使用dynamic_cast来检查它是否真的是dA。现在,您是否在那时将结果保存在m_dA或稍后再次进行动态转换是不重要的。初始化dB的可能性较小,其动态转换可能会在以后失败。