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

Posted

技术标签:

【中文标题】将重载函数与其多态参数匹配【英文标题】:Matching an overloaded function to its polymorphic argument 【发布时间】:2011-10-17 09:08:04 【问题描述】:

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

我在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++ 中做一些不同的事情来实现它吗?

谢谢,

加里。

【问题讨论】:

【参考方案1】:

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 中放置一个特定于简单的调度函数,或者您可以忽略此技术,而是在SimpleOpenGLRenderer::renderModel 中重复使用dynamic_cast 来测试类型.

【讨论】:

谢谢。我以前没有使用过这种模式。我想这在 Java 中可能不是很常见。我已经做了一些测试,我想我可能会退回到dynamic_cast 解决方案。我希望有更优雅的东西,但双重调度/访问者模式似乎引入了我不太热衷的各种依赖关系和紧密耦合。【参考方案2】:

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

你需要的可能是double-dispatch 机制,它非常接近访问者模式。阅读这些:

Double Dispatch Visitor Pattern

【讨论】:

【参考方案3】:

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

【讨论】:

啊,谢谢你的链接,我忘了。所以,现在,只是偷了你的链接。呵呵。 :-)【参考方案4】:

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

您可以通过两种方式使用运行时类型检查。一种是使用dynamic_cast&lt;&gt;,另一种是在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();
  
;

【讨论】:

我想我会使用dynamic_cast 解决方案,因为考虑到静态类型解析没有更好的解决方案。我不希望 Models 负责渲染,因为我已经有 10 个不同的 Renderers 以不同的方式执行渲染。【参考方案5】:

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

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

我认为这里最简洁的方法是所谓的visitor pattern。您的SimpleOpenGLRenderer 可以调用方法model.renderOn( *this )。那么Model::renderOn 要么是一组重载,一个用于每种可能的渲染器类型,要么是一个使用dynamic_cast 发现渲染器类型的虚拟方法。无论如何,它会在渲染器上回调,但现在该调用知道它是什么类型的渲染器以及它本身是什么类型,并且还可以调用一个非常具体的渲染方法,例如,@987654326 @。

干杯,

【讨论】:

【参考方案6】:

这里的其他解决方案将完全满足您的需求。但在我看来,以复杂性为代价。如果您的问题与描述的完全一样,我建议您更改解决方案的架构。渲染器不是在尝试完成模型的工作吗?我看到的是一种重载生成的switch语句。

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

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

【讨论】:

我不认为这比其他方法更简单。如果我使用“工具”进行渲染,那么“渲染器”是做什么用的?感觉就像一个不受欢迎的中间人。我不希望Model 负责渲染。渲染可以通过多种不同的方式完成,甚至可以使用不同的图形 API。 Model 的职责只是描述几何图形。 我尽量坚持信息专家的原则。在这种情况下,模型是唯一知道形状如何的人。打开类型以使外部对象进行绘图是 IMO 对封装的不必要破坏。渲染器的用途是一个有效的问题。 IMO 也许模型应该使用渲染器,反之亦然。

以上是关于将重载函数与其多态参数匹配的主要内容,如果未能解决你的问题,请参考以下文章

重载的编程语言中的重载

虚函数和函数重载的问题

重载与多态

C++

3.多态性

java中重载,继承,重写和多态的区别