好的,标题是满口的,我认为这可能是为什么很难通过谷歌或这个网站找到答案。可能只是因为我不知道如何正确地表达问题,但这里有:
我在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 ++中做些不同的事情来实现吗?
谢谢,
加里。
答案 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)
答案 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;
};