问题描述
假设我有一堆水果:
class Fruit { ... };
class Apple : public Fruit { ... };
class Orange: public Fruit { ... };
以及对所述水果起作用的一些多态函数:
And some polymorphic functions that operate on said fruit:
void Eat(Fruit* f, Pesticide* p) { ... }
void Eat(Apple* f, Pesticide* p) { ingest(f,p); }
void Eat(Orange* f, Pesticide* p) { peel(f,p); ingest(f,p); }
好的,等等.停在那儿.请注意,这时任何有理智的人都会使Eat()成为Fruit类的虚拟成员函数.但这不是一个选择,因为我不是一个理智的人.另外,我不想在我的水果课的头文件中使用该农药*.
OK, wait. Stop right there. Note at this point that any sane person would make Eat() a virtual member function of the Fruit classes. But that's not an option, because I am not a sane person. Also, I don't want that Pesticide* in the header file for my fruit class.
可悲的是,我接下来要做的正是成员函数和动态绑定所允许的:
Sadly, what I want to be able to do next is exactly what member functions and dynamic binding allow:
typedef list<Fruit*> Fruits;
Fruits fs;
...
for(Fruits::iterator i=fs.begin(), e=fs.end(); i!=e; ++i)
Eat(*i);
显然,这里的问题是我们传递给Eat()的指针将是Fruit *,而不是Apple *或Orange *,因此什么也不会被吃掉,我们都会非常饿.
And obviously, the problem here is that the pointer we pass to Eat() will be a Fruit*, not an Apple* or an Orange*, therefore nothing will get eaten and we will all be very hungry.
所以我真正想做的是代替这个:
So what I really want to be able to do instead of this:
Eat(*i);
是这个
Eat(MAGIC_CAST_TO_MOST_DERIVED_CLASS(*i));
但是据我所知,这种魔力不存在,除非它可能是一个充满讨厌的if语句的形式,充满了对dynamic_cast的调用.
But to my limited knowledge, such magic does not exist, except possibly in the form of a big nasty if-statement full of calls to dynamic_cast.
那么有一些我不知道的运行时魔术吗?还是应该实施并维护一个充满dynamic_casts的讨厌的if语句?还是我应该把它吸干,放弃思考如何在Ruby中实现它,并允许一些农药进入我的水果标头中?
So is there some run-time magic of which I am not aware? Or should I implement and maintain a big nasty if-statement full of dynamic_casts? Or should I suck it up, quit thinking about how I would implement this in Ruby, and allow a little Pesticide to make its way into my fruit header?
更新:假设我只是不想将Eat放入水果中,因为它没有意义,而不是仅仅使用Eat功能和农药来做些麻烦.会自己吃的水果? sha相反,我需要一个带有Eat函数的Eater类,使用不同的代码来食用每种水果,以及一些默认代码,以防食者无法识别出这种水果:
Update: Instead of the contrived bit with the bare Eat functions and Pesticide, suppose instead that I just don't want to put Eat in the fruit because it makes no sense. A fruit that knows how to eat itself? Pshaw. Instead I need an Eater class with an Eat function, with different code for eating each kind of fruit, and some default code in case it's a fruit that the eater doesn't recognize:
class Eater
{
public:
void Eat(Apple* f) { wash(); nom(); }
void Eat(Orange* f) { peel(); nom(); }
void Eat(Fruit* f) { nibble(); }
};
...
Eater me;
for(Fruits::iterator i=fs.begin(), e=fs.end(); i!=e; ++i)
me.Eat(*i); //me tarzan! me eat!
但是再次,这是行不通的,C ++中直接的解决方案似乎是对dynamic_cast的大量调用.
But again, this doesn't work, and the straightforward solution in C++ seems to be a bunch of calls to dynamic_cast.
但是,正如答案之一所示,可能还有另一个聪明的解决方案.如果Fruits通过MustPeel()和MustWash()之类的功能暴露了食者重要的素质,该怎么办?然后,您可以使用一个Eat()函数...
However, as one of the answers suggests, there may be another clever solution. What if Fruits exposed the qualities that mattered to eaters, with functions like MustPeel() and MustWash()? Then you could get by with a single Eat() function ...
更新:丹尼尔·纽比(Daniel Newby)指出,使用Visitor也可以解决所呈现的问题...但这需要一些语义上的倒立(Fruit :: use或Fruit :: beEaten?).
Update: Daniel Newby points out that using Visitor also solves the problem as presented ... but this requires a bit of a semantic headstand (Fruit::use or Fruit::beEaten?).
虽然我想接受几个答案,但我认为psmears的答案实际上是将来读者的最佳答案.谢谢大家.
While I'd like to accept several answers, I think psmears's answer is actually the best one for future readers. Thanks, everyone.
推荐答案
当出现这样的问题时,最好确切地查看为什么您要做出特定决定的为什么-例如,为什么,您不希望Fruit类了解农药吗?
When a question like this comes up, it's good to look at exactly why you want to make particular decisions - for instance, why do you not want the Fruit classes to know about Pesticide?
我敢肯定,这是一个很好的理由-但是表达这一理由将有助于在您的脑海中确切地阐明您的目标-这通常为可能的角度提供了新的思路.构建程序.
I'm sure there is a good reason for this - but expressing that reason will help clarify in your mind exactly what your aims are - and this often sheds a new light on a possible angle for structuring the program.
例如,您可能最终会添加新的虚拟方法"IsEdible"和"PrepareForEating".然后,您可以为每种水果实施这些操作,并实施一种适用于所有水果的通用Eat方法-并也摄取讨厌的农药-所有这些都不需要Fruit类对此有所了解.
For instance, you might end up adding new virtual methods "IsEdible" and "PrepareForEating". Then you can implement these for each fruit, and implement one generic Eat method that works for all fruits - and ingests the pesky pesticide too - all without the Fruit classes knowing anything about it.
当然,根据您的确切目标,这可能是完全不合适的-这就是为什么您必须自己弄清楚示例的原因:-)
Of course, depending on your precise aims, that may be totally inappropriate - which is why you'll have to clarify the example in your own head :-)
这篇关于苹果,橙子和指向最派生的c ++类的指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!