将重载函数与其多态参数匹配

时间:2011-08-01 11:20:51

标签: c++ function polymorphism overloading

好的,标题是满口的,我认为这可能是为什么很难通过谷歌或这个网站找到答案。可能只是因为我不知道如何正确地表达问题,但这里有:

我在SimpleOpenGLRenderer类中有一系列方法,它们都采用扩展Model类的单个参数。因此,根据模型的类型,渲染器将调用知道如何渲染它的正确方法。这是一个基于问题的简化可执行示例:

#include <stdio.h>

class Model {};

class Cube : public Model {};

class Sphere : public Model {};

class Renderer
{
  public:
    virtual void renderModel(const Model& model) = 0;
};

class SimpleOpenGLRenderer
{
  public:
    void renderModel(const Cube& model)
    {
      printf("Render the cube.\n");
    }

    void renderModel(const Model& model)
    {
      printf("Throw an exception, my renderer does not support the model type you have provided.\n");
    }

    void renderModel(const Sphere& model)
    {
      printf("Render the sphere.\n");
    }
};

int
main(int argc, char** argv)
{
  Cube cube;
  Model& model = cube;
  SimpleOpenGLRenderer renderer;

  renderer.renderModel(cube);
  renderer.renderModel(model);
}

示例的输出是:

Render the cube.
Throw an exception, my renderer does not support the model type you have provided.

对于一位经验丰富的C ++开发人员来说,这似乎显而易见,但这并不是按计划运行的,但这对我来说没有意义。在运行时,我不知道传递给渲染器的Model的确切类型(因此尝试重载以解决它)。来自Java背景,我在Java之前和之前使用过这种技术,调用的方法最适合参数的 runtime 类型。在C ++中,它似乎与引用的编译时类型匹配,即使该引用最终可能是一个子类 - 我认为 - 更好地匹配另一个函数。

到目前为止,我已将此运行时类型匹配视为理所当然。它是否根本不存在于C ++中,还是我以错误的方式解决这个问题?我应该在C ++中做些不同的事情来实现吗?

谢谢,

加里。

6 个答案:

答案 0 :(得分:17)

C ++中的重载在编译时根据参数的静态类型解析。

有一种可能有用的技术称为“双重调度”:

class Model {
    virtual void dispatchRender(Renderer &r) const = 0;
};

class Cube : public Model {
    virtual void dispatchRender(Renderer &r) const {
        r.renderModel(*this); // type of "this" is const Cube*
};

int main() {
    Cube cube;
    Model &model = cube;
    SimpleOpenGLRenderer renderer;
    cube.dispatchRender(renderer);
}

请注意,Renderer基类需要包含SimpleOpenGLRenderer当前所做的所有重载。如果您希望它特定于SimpleOpenGLRenderer存在什么重载,那么您可以在Model中放置一个特定于Simple的调度函数,或者您可以忽略此技术,而是在dynamic_cast中重复使用SimpleOpenGLRenderer::renderModel {1}}测试类型。

答案 1 :(得分:2)

在您的代码中,函数重载将根据参数的静态类型进行解析。

您需要的是double-dispatch机制,它与访客模式非常接近。阅读这些:

答案 2 :(得分:1)

对于基于动态类型的“运行时重载”,可以使用visitor pattern

答案 3 :(得分:1)

如果您使用它,您的代码是运行时类型匹配的理想选择。在这里,您将Cube收到Model&并将其传递给renderModel()。到目前为止,您还没有机会让编译器使用运行时类型。而是依赖于对象的静态类型。

您可以通过两种方式使用运行时类型检查。一个是使用dynamic_cast<>,另一个是在Model中提供接口方法。即。

class Model {
  virtual void print () { printf("throw..."); }  // provide an interface method
};

class Cube : public Model {
  virtual void print () { print("Render cube\n"; }  // implement it
};

class Sphere : public Model {
  virtual void print () { print("Render sphere\n"; }  // implement it
};

class SimpleOpenGLRenderer
{
  public:
  void renderModel(const Model& model)
  {
    model.print();
  }
};

答案 4 :(得分:0)

在C ++中,调用重载的解析是在编译时完成的。

要使有效实现依赖于多态参数类型,您需要查询该参数,即在参数上调用虚方法。

我认为这里最干净的方法就是所谓的visitor pattern。您的SimpleOpenGLRenderer可以调用方法model.renderOn( *this )。然后Model::renderOn是一组重载,一个用于每种可能的渲染器类型,或者它是一个使用dynamic_cast来发现渲染器类型的虚拟方法。无论如何,它然后在渲染器上回调,但现在该调用知道它是什么类型的渲染器以及它本身是什么类型,并且还可以调用非常特定的渲染方法,例如{{ 1}}。

干杯,

答案 5 :(得分:0)

这里的其他解决方案将完全符合您的要求。但在我看来,以复杂性为代价。如果您的问题与描述完全相同,我建议您更改解决方案的体系结构。渲染器是不是试图完成模型的工作?我所看到的是一种过载产生的切换句子。

如何让模型渲染自己,也许是通过使用一些提供更原始绘图方法的类:

class Cube : public Model {
  render(RenderTool& tool) {
    tool.renderCube(); //or even more low level
  }
};

class SimpleOpenGLRenderer {
  public:
  RenderModel(Model& model) {
    model.render(renderTool);
  }
  private:
  SomeOpenGLRenderingTool renderTool;
};