将重载函数与其多态参数匹配
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
机制,它非常接近访问者模式。阅读这些:
【讨论】:
【参考方案3】:对于基于动态类型的“运行时重载”,可以使用visitor pattern。
【讨论】:
啊,谢谢你的链接,我忘了。所以,现在,只是偷了你的链接。呵呵。 :-)【参考方案4】:如果您使用它,您的代码是运行时类型匹配的良好候选者。在这里,您将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();
;
【讨论】:
我想我会使用dynamic_cast
解决方案,因为考虑到静态类型解析没有更好的解决方案。我不希望 Model
s 负责渲染,因为我已经有 10 个不同的 Renderer
s 以不同的方式执行渲染。【参考方案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 也许模型应该使用渲染器,反之亦然。以上是关于将重载函数与其多态参数匹配的主要内容,如果未能解决你的问题,请参考以下文章