C++ : Add a method to a base class interface

标签 c++ polymorphism

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

基础 => Sub1、Sub2、Sub3 => SubSub1、SubSub2 ... 等等

我可以从层次结构中的各个子类中获取一堆对象,将 *Base 类型的指针填充到 STL 容器中,然后使用迭代器在每个对象上调用特定的基类方法。

如果我想向基类添加一个新的虚拟方法,然后执行相同的操作,为容器中的每个对象调用该方法,该怎么办?

基类是库的一部分,因此我不能只向其中添加新的虚拟方法。派生子类不起作用,因为我无法访问所有其他子类。在 Java 中,我将创建一个接口(interface)并让每个相关子类实现它。不过,我不确定如何在 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. 您可以在适当的命名空间中创建一个全局函数或一个函数,以在给定 Base 类型的对象的情况下执行操作。
  2. 您提供了一种机制,派生类型的实现可以在其中自行注册。
  3. 在函数的实现中,您将迭代已注册的函数/仿函数以检查是否存在对象类型的实现。如果是,则执行该操作。否则,您会报告错误。

假设您有:

struct Shape
{
    // Shape details
};

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

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

出于说明目的,假设 Shape 没有用于计算 Shape 对象面积的接口(interface)。要实现计算形状面积的功能,您可以执行以下操作:

  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. 添加函数来计算三角形矩形的面积,只要在代码库中合适的地方即可。

    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 block 。
  2. 它避免了核心中处理派生类型的硬依赖。

缺点

  1. 最重要的是 - 不要将其用于任何需要调用数百万次的函数。这会降低性能。

关于C++ : Add a method to a base class interface,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26892008/

相关文章:

haskell - GADT 扩展会破坏多态性吗?

c# - 无法在 C# 中创建两个具有相同名称的属性

c++ - 在 GDB 中运行应用程序直到发生异常

Java 类转换规则

c++ - CaptureStackBackTrace 是单线程的吗?

c++ - OpenCV标定麻烦

c++ - libjpeg:如何获得正确的图像解压大小?

c++ - Visual-C++ malloc 实现?

android - 如何以编程方式将 44100 立体声转换为 11025 单声道?

java - 在 ArrayList<? 定义的方法中交换两个子级扩展父级>