C++ 组合与继承设计问题
我从一个实现了抽象类形式的算法的库中获得了这些虚拟类C++ 组合与继承设计问题,c++,oop,design-patterns,C++,Oop,Design Patterns,我从一个实现了抽象类形式的算法的库中获得了这些虚拟类 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 ; }; 要使用库
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()
)
我通常会通过调用
dB * mydB = new dB() ;
mydB->setA(new dA() );
mydB->f() ;
mydB->g() ;
但是,在实现dB::f()
时,我遇到了一个设计问题,因为我需要调用dA::bar()
,它特定于dB
。但在课堂上,我只通过AB*保留对dB的引用。然后我想到了两个选择:
- 每次调用
时,使用f()
将dynamic\u cast
转换为B::m\u a
dB*
- 向dB添加一个m_dA成员,该成员存储与m_a相同的指针,但可用于访问特定于dB的函数
我想知道这个问题是否有更优雅的解决方案(比如我没有想到的设计模式)。如果没有,我应该选择哪一个?您有第三个解决方案。在
dB
中添加函数setA()。当然,此函数将隐藏B::setA()
,如果将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类:公共B类
{
dA*m_dA;//也添加此成员!
公众:
void f()
{
m_dA->bar();//快进:这里没有演员!
}
void setA(A*A)//这将隐藏B::setA()
{
m_dA=dynamic_cast(a);//只需一次dynamic cast!
如果(m_dA==0)
{
抛出std::runtime_错误(“无效参数”);
}
setA(a);//现在调用基中的隐藏函数!
}
};
这样,您就不需要在每次调用dB::f()
时进行动态\u强制转换,这使得调用很快 您还有第三种解决方案。在dB
中添加函数setA()。当然,此函数将隐藏B::setA()
,如果将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类:公共B类
{
dA*m_dA;//也添加此成员!
公众:
void f()
{
m_dA->bar();//快进:这里没有演员!
}
void setA(A*A)//这将隐藏B::setA()
{
m_dA=dynamic_cast(a);//只需一次dynamic cast!
如果(m_dA==0)
{
抛出std::runtime_错误(“无效参数”);
}
setA(a);//现在调用基中的隐藏函数!
}
};
这样,您就不需要在每次调用dB::f()
时进行动态\u强制转换,这使得调用很快 只有当数据库的A实际上是dA时,它才能工作。你需要确保情况始终如此
因此,超越setA方法,使用动态_转换来检查它是否真的是dA。现在,无论你在那一点上保存的结果在m_dA或动态铸造后再次是不重要的。不太可能出现初始化错误的dB,其动态转换稍后可能会失败。只有当dB的A实际上是dA时,它才能工作。你需要确保情况始终如此
因此,超越setA方法,使用动态_转换来检查它是否真的是dA。现在,无论你在那一点上保存的结果在m_dA或动态铸造后再次是不重要的。不太可能出现初始化错误的dB,其动态强制转换稍后可能会失败。dB
作为一个具体类,不应该在不相关的具体类上调用方法。即使使用reinterpret_cast
这也是一种糟糕的设计,它不必要地耦合了不相关的对象。公共功能应该放在公共基类或接口中
例如,类A
可以被视为一个接口,因为它只有纯虚拟方法。因此,如果您还希望在dB
上使用该接口,那么使用多重继承是安全的。在这种情况下,您当然必须实现自己的foo
class dB : public B, public A {
public :
void f();
void foo();
};
如果bar()
是您想要的,并且出于某种原因,您可以更改接口A
,然后创建您自己的接口,该接口提供纯虚拟函数bar()
,然后使dA
和dB
从新接口继承并实现bar()
并相应地使用接口指针
如果您必须在另一个方面使用其中一个,那么组合是一种方法,但不能使用可能导致动态强制转换失败的模棱两可的基指针。可能在dB
中创建一个具体的dA
成员。dB
作为一个具体类,不应该调用与dA
无关的具体类上的方法。即使使用reinterpret_cast
这也是一种糟糕的设计,它不必要地耦合了不相关的对象。公共功能应该放在公共基类或接口中
例如,类A
可以被视为一个接口,因为它只有纯虚拟方法。因此,如果您还希望在dB
上使用该接口,那么使用多重继承是安全的。在这种情况下,您当然必须实现自己的foo
class dB : public B, public A {
public :
void f();
void foo();
};
如果bar()
是您想要的,并且出于某种原因,您可以更改接口A
,然后创建您自己的接口,该接口提供纯虚拟函数bar()
,然后使dA
和dB
从新接口继承并实现bar()
并相应地使用接口指针
如果你必须用一个来表示另一个,那么合成是一条路要走,但不能用模棱两可的基指针