类设计复杂度(C++)

Class design complication (C++)

本文关键字:C++ 复杂度      更新时间:2023-10-16

我的类是

  • Base
    • Derived_A
    • Derived_B
  • Parent
    • Child_One
    • Child_Two

Base有两个签名函数:

virtual void foo( const Parent& ) = 0;
virtual void bar( const Base& ) = 0;

,这是程序的其他部分所期望的。

问题是:

CCD_ 7对CCD_ 8和CCD_。但Derived_B对它们的处理方式不同。

我应该如何实现这一点?

一种方法是找出什么样的对象被传递给CCD_ 11。这显然是"设计缺陷"。我尝试的另一种方法是将签名函数更改为:

class Base
{
class Derived_A;
class Derived_B;
//  virtual void bar( const Base& ) = 0;
virtual void bar( const Derived_A& ) = 0;
virtual void bar( const Derived_B& ) = 0;
}
class Derived_A: public virtual Base
{ 
virtual void foo( const Parent& ) = 0;
}
class Derived_B: public virtual Base
{ 
virtual void foo( const Child_A& ) = 0;
virtual void foo( const Child_B& ) = 0;
}

但是现在bar函数不能使用Base.foo。所以我不得不写两次bar函数,尽管代码完全相同。

有其他方法来处理这个问题吗?你建议哪一个?

附言:我想不出一个好的头衔。请随时修改。

您所描述的问题称为双重调度。该链接描述了这个问题和一些可能的解决方案(包括多态函数签名和访问者模式)。

如果没有两个类型层次结构之间的关系以及它们如何交互的详细信息,就不可能说出什么方法是合适的。我已经对其他答案和另一个可行的替代方案进行了概述,可以扩展到评论中提到的访问者模式。

正如Joey Andres已经提出的那样,在Parent中实现virtual函数的子代中执行多态行为是这个问题的典型的面向对象解决方案。它是否合适,取决于对象的责任。

Olayinka建议的类型检测,以及你的问题中已经提到的,闻起来肯定很糟糕,但根据细节,可能是N种邪恶的最小值。它可以用返回enum的成员函数来实现(我想这就是Olayinka的答案试图表示的),也可以用一系列dynamic_cast来实现,如您链接的问题中的一个答案所示。

一个简单的解决方案可能是在Base:中过载foo

struct Base {
virtual void foo(const Parent&) = 0;
virtual void foo(const Child_Two&) = 0;
};
struct Derived_A: Base { 
void foo(const Parent& p) {
// treat same
}
void foo(const Child_Two& p) {
foo(static_cast<Parent&>(p));
}
};
struct Derived_A: Base { 
void foo(const Parent& p) {
// treat Child_One (and other)
}
void foo(const Child_Two& p) {
// treat Child_Two
}
};

如果Base的其他亚型对Child_OneChild_Two的处理相同,则foo(const Child_Two&)的实现可以放在Base中以避免重复。

这种方法的缺点是必须使用正确静态类型的引用来调用foo。调用将不会基于动态类型进行解析。这对你的设计可能是好是坏。如果你需要多态行为,你可以使用访问者模式,它本质上是在上面的解决方案之上添加虚拟调度:

struct Base {
foo(Parent& p) {
p.accept(*this);
}
virtual void visit(Child_A&) = 0;
virtual void visit(Child_B&) = 0;
};
struct Parent {
virtual void accept(Base&) = 0;
};
struct Child_A: Parent {
void accept(Base& v) {
v.visit(*this);
}
};
// Child_B similarly
struct Derived_A: Base { 
void treat_same(Parent&) {
// ...
}
void visit(Child_A& a) {
treat_same(a);
}
void visit(Child_B& b) {
treat_same(b);
}
};
struct Derived_B: Base { 
void visit(Child_A&) {
// ...
}
void visit(Child_B&) {
// ...
}
};

还有更多的样板,但由于你似乎非常反对在孩子身上实施这种行为,这可能对你来说是个好方法。

您本可以在Parent中轻松地创建virtual foo方法。既然您希望Derive_A对所有Parent的子类一视同仁,为什么不在Parent中实现一个这样做的类呢。这是最合乎逻辑的事情,因为如果你想对他们两个都做同样的事情,那么他们两个必须有相似的数据,这在Parent中是存在的。

class Parent{
virtual void treatSame(){
// Some operations that treat both Child_A, and Child_B
// the same thing to both Child_A and Child_B.
}
virtual void foo() = 0;
}

由于您希望Derived_B在Child_A和Child_B中执行不同的操作,请利用多态性。考虑下面的其他类:

class Child_A : public Parent{
virtual void foo(){
// Foo that is designed for special Child_A.
}
}
class Child_B : public Parent{
virtual void foo(){
// Foo that is designed for special Child_B.
}
}

class Base{
virtual void foo(Parent) = 0;
virtual void bar(Base) = 0;
}
class Derived_A: public Base
{ 
virtual void foo( Parent& p){
p.treatSame();
}
}
class Derived_B: public Base
{ 
virtual void foo( Parent& p){
p.foo();  // Calls appropriate function, thanks to polymorphism.
}
}

一种可能的用法如下:

int main(){
Child_A a;
Child_B b;
Derived_A da;
da.foo(a);  // Calls a.treatSame();
da.foo(b);  // Calls a.treatSame();
Derived_B db;
db.foo(a);  // Calls a.foo();
db.foo(b);  // Calls b.foo();
}

请注意,只有当参数是指针或引用时,这才会起作用(我更喜欢在可能的情况下处理引用)。否则,虚拟调度(选择适当的功能)将无法工作。

我不确定语法,但你明白要点了。

class Base{
virtual void bar( Base ) = 0;
virtual void foo( Parent ) = 0;
}
class Derived_A: public virtual Base{ 
virtual void foo( Parent ) = 0;
}
class Derived_B: public virtual Base{ 
virtual void foo( Parent ){
//switch case also works
return parent.get_type() == Parent::TYPE_A ? foo_A((Child_A)parent) : foo_B((Child_B)parent);
}
virtual void foo_A( Child_A ) = 0;
virtual void foo_B( Child_B ) = 0;
}
class Parent{
virtual int get_type() = 0;
}
class Child_A: public virtual Parent{ 
return Parent::TYPE_A;
}
class Child_B: public virtual Parent{ 
return Parent::TYPE_B;
}