2011-08-01 30 views
17

好的,标题很满意,我认为这可能是为什么它很难通过谷歌或本网站找到答案。这可能只是我不知道如何正确表达问题,但这里有:将重载函数匹配到其多态参数

我在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中,所调用的方法将与参数的类型运行时最匹配。在C++中,它似乎与引用的编译时类型相匹配,即使该引用可能最终成为某个子类 - 在我看来 - 更好地匹配另一个函数。

直到现在我已经采取这种运行时类型匹配为理所当然。它在C++中是不是存在,还是我以错误的方式去做?我应该在C++中做不同的事情来实现它吗?

谢谢,

Gary。

回答

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

+2

谢谢。我以前没有使用过这种模式。我猜这在Java中可能不常见。我做了一些测试,我想我可能会回到'dynamic_cast'解决方案。我希望有更优雅的东西,但双派/访客模式似乎引入了我不太喜欢的各种依赖和紧密耦合。 –

1

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

+0

啊,谢谢你的链接,我忘记了。所以,现在,只是偷了你的链接。呵呵。 :-) –

2

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

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

1

您的代码运行时类型匹配的一个不错的选择,如果你使用它。在这里,您将Cube收到Model&中,并简单地将其传递给renderModel()。直到现在,您还没有机会让编译器使用运行时类型。而是依赖于对象的静态类型。

用2种方法可以使用运行时类型检查。一种是使用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(); 
    } 
}; 
+0

考虑到静态类型的分辨率,我想我会使用'dynamic_cast'解决方案,因为缺乏更好的解决方案。我不希望'模型'负责渲染,因为我已经有10个不同的'Renderer'已经以不同的方式执行渲染。 –

0

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

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

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

干杯,

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; 
}; 
+0

我没有看到这比其他方法简单。如果我使用“工具”来渲染渲染,那么渲染器是什么?这感觉就像一个不需要的中间人。我不希望'Model'负责渲染。渲染可以通过多种不同的方式完成,甚至可以使用不同的图形API。 “模型”的责任仅仅是描述几何。 –

+0

我尽量遵守信息专家的原则。在这种情况下,模型是唯一知道形状的人。打开类型来创建一个外部对象的绘图是IMO不必要的封装破坏。渲染器的用途是一个有效的问题。 IMO也许应该使用渲染器而不是反之。 – daramarak