簡體   English   中英

類設計復雜性(C ++)

[英]Class design complication (C++)

我的課程是

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

Base有兩個簽名功能:

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

,該計划的其他部分期望。

問題是:

Derived_AChild_OneChild_Two的處理Child_One相同。 Derived_B對待它們的方式不同。

我該如何實現呢?

一種方法是找出傳遞給Derived_B.foo的對象Derived_B.foo 這顯然是“設計缺陷”。 我試過的另一種方法是將簽名函數更改為:

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函數,盡管代碼完全相同。

還有其他方法可以解決這個問題嗎? 你建議哪一個?

PS我想不出一個好頭銜。 請隨時修改它。

您描述的問題稱為Double Dispatch 該鏈接描述了問題以及解決方案的一些可能方法(包括多態函數簽名和訪問者模式)。

您可以在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();
}

請注意,這僅在參數為指針或引用時才有效(我希望在可能的情況下處理引用)。 虛擬調度(選擇適當的功能)將不起作用。

如果沒有關於兩種類型層次結構之間的關系以及它們如何相互作用的細節,就不可能說出哪種方法是合適的。 我已經概述了其他答案和另一個可行的替代方案,可以擴展到評論中提到的訪客模式。

正如Joey Andres已經建議的那樣,在Parent實現virtual函數的子節點中執行多態行為通常是針對此問題的典型面向對象解決方案。 它是否合適取決於對象的責任。

Olayinka建議的類型檢測已經在你的問題中提到了肯定聞到了kludgy,但根據細節,可以是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必須使用適當的靜態類型引用來調用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&) {
        // ...
    }
};

還有一些樣板,但由於你似乎非常反對在孩子們中實現這種行為,這對你來說可能是個好方法。

我不確定語法,但你得到了主旨。

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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM