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

时间:2014-11-12 16:40:39

标签: c++ polymorphism

假设我有一个具有大型多态类层次结构的第三方库:

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

2 个答案:

答案 0 :(得分:2)

实际上有两种方法可以实现这一目标。

1)添加一个类(比如base1),其中base将是库中的“基类”。然后让所有其他类派生自base1而不是base。

2)使用多重继承。你添加另一个类“base1”,然后让其他派生类继承“base”和“base1”。

我更喜欢以前的方法,因为多重继承有它自己的瓶颈。

答案 1 :(得分:1)

如果您没有修改基类的选项,可以使用我称之为插件模式的模式。

  1. 您可以在适当的命名空间中创建全局函数或函数,以便在给定Base类型的对象的情况下执行操作。
  2. 您提供了一种机制,其中派生类型的实现可以自行注册。
  3. 在函数的实现中,您遍历已注册的函数/仿函数以检查是否存在对象类型的实现。如果是,则执行操作。否则,您报告错误。
  4. 假设你有:

    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);
      
    7. <强>赞成

      1. 这是一种在一个函数中避免长if-else块的复杂方法。
      2. 它避免了核心中的硬依赖关系来处理派生类型。
      3. <强>缺点

        1. 最重要的是 - 不要将此用于任何需要被调用数百万次的函数。这会扼杀表现。