簡體   English   中英

C ++:向基類接口添加方法

[英]C++ : Add a method to a base class interface

假設我有一個具有大型多態類層次結構的第三方庫:

Base => Sub1,Sub2,Sub3 => SubSub1,SubSub2 ......等

我可以從層次結構中的各個子類中獲取一堆對象,將* Base類型的指針填充到STL容器中,然后使用迭代器在每個子類上調用特定的基類方法。

如果我想在基類中添加一個新的虛方法然后執行相同的操作,為容器中的每個對象調用該方法,該怎么辦?

基類是庫的一部分,所以我不能只為它添加一個新的虛方法。 派生子類不起作用,因為我無法訪問所有其他子類。 在Java中,我將創建一個接口,並讓每個相關的子類實現它。 但我不確定如何最好地在C ++中處理這個問題。

編輯:

(1)下面建議的訪問者模式將是一個很好的解決方案,但要求在原始基類編寫時考慮到該模式。

(2)下面提出的插件模式是一種有效的通用解決方案,但在某些用例中可能會非常慢。

(3)從Base派生一個子類,然后重構整個層次結構,使它從這個子類派生,這很麻煩,如果庫代碼升級,可能會中斷。

(4)我盡量避免多重繼承,但它適用於我的(簡單)用例:

#include <third_party_lib.h>

class MyBase {
  public:
    virtual ~MyBase() {}
    virtual void myMethod() = 0;
};

class MySub1 : public ThirdPartyLib::Sub1, MyBase {
  public:
    void myMethod() { /*...*/ }
};

class MySub2 : public ThirdPartyLib::Sub2, MyBase {
  public:
    void myMethod() { /*...*/ }
};

void doSomething() {
  std::vector<ThirdPartyLib::Base*> vec;
  // fill vector with instances of MySub1, MySub2, etc
  for (auto libHandle : vec) {
    // call a method from the library class hierarchy ...
    libHandle->libraryClassMethod();
    // call the virtual method declared in MyBase ...
    MyBase* myHandle = dynamic_cast<MyBase*>(libHandle);
    if (myHandle) {
      myHandle->myMethod();
    } else {
      // deal with error
    }
  }  
}

實際上有兩種方法可以實現這一目標。

1)添加一個類(比如base1),其中base將是庫中的“基類”。 然后讓所有其他類派生自base1而不是base。

2)使用多重繼承。 你添加另一個類“base1”,然后讓其他派生類繼承“base”和“base1”。

我更喜歡以前的方法,因為多重繼承有它自己的瓶頸。

如果您沒有修改基類的選項,則可以使用我稱之為插件模式的模式。

  1. 您可以在適當的命名空間中創建全局函數或函數,以便在給定Base類型的對象的情況下執行操作。
  2. 您提供了一種機制,其中派生類型的實現可以自行注冊。
  3. 在函數的實現中,您遍歷已注冊的函數/仿函數以檢查是否存在對象類型的實現。 如果是,則執行操作。 否則,您報告錯誤。

假設你有:

struct Shape
{
    // Shape details
};

struct Triangle : public Shape
{
    // Triangle details
};

struct Rectangle : public Shape
{
    // Rectangle details
}; 

出於說明的目的,假設Shape沒有用於計算Shape對象區域的接口。 要實現計算形狀區域的功能,您可以執行以下操作:

  1. 創建一個函數來獲取Shape區域。

     extern double getArea(Shape const& shape); 
  2. 為可以計算形狀區域的函數添加注冊機制。

     typedef double (*GetAreaFunction)(Shape const& shape, bool& isSuccess); extern void registerGetAreaFunction(GetAreaFunction fun); 
  3. 在.cc文件中實現核心功能。

     static std::set<GetAreaFunction>& getRegistry() { static std::set<GetAreaFunction> registry; return registry; } void registerGetAreaFunction(GetAreaFunction fun) { getRegistry().insert(fun); } double getArea(Shape const& shape) { double area = 0.0; for ( auto fun: getRegistry() ) { bool isSuccess = false; area = fun(shape, isSuccess); if ( isSuccess ) { return area; } } // There is no function to compute the area of the given shape. // Throw an exception or devise another mechanism to deal with it. } 
  4. 添加函數來計算TriangleRectangle的區域,無論它在您的代碼庫中看起來是否合適。

     double getArea(Triangle const& triangle) { // Do the needful and return the area. } double getArea(Rectangle const& rectangle) { // Do the needful and return the area. } 
  5. 添加可以在核心API中注冊的功能。

     double getAreaWrapper(Shape const& shape, bool& isSuccess) { // Do dynamic_cast to see if we can deal with the shape. // Try Triangle first. Triangle const* trianglePtr = dynamic_cast<Triangle const*>(&shape); if ( trianglePtr ) { isSuccess = true; return getArea(*trianglePtr); } // Try Rectangle next. Rectangle const* rectanglePtr = dynamic_cast<Rectangle const*>(&shape); if ( rectanglePtr ) { isSuccess = true; return getArea(*rectanglePtr ); } // Don't know how to deal with the given shape. isSuccess = false; return 0.0; } 
  6. 使用核心注冊功能。

     registerGetAreaFunction(getAreaWrapper); 

優點

  1. 這是一種用於避免一個函數中的長if-else塊的復雜方法。
  2. 它避免了核心中的硬依賴性來處理派生類型。

缺點

  1. 最重要的是 - 不要將它用於任何需要被調用數百萬次的函數。 這會扼殺性能。

暫無
暫無

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

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