假设我有一堆水果:
class Fruit { ... };
class Apple : public Fruit { ... };
class Orange: public 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 类的虚拟成员函数。但这不是一个选择,因为我不是一个理智的人。另外,我不想在我的水果类的头文件中使用 Pesticide*。
可悲的是,我接下来想要做的正是成员函数和动态绑定所允许的:
typedef list<Fruit*> Fruits;
Fruits fs;
...
for(Fruits::iterator i=fs.begin(), e=fs.end(); i!=e; ++i)
Eat(*i);
很明显,这里的问题是我们传递给 Eat() 的指针将是 Fruit*,而不是 Apple* 或 Orange*,因此没有东西会被吃掉,我们都会非常饿。
所以我真正想做的不是这个:
Eat(*i);
这是:
Eat(MAGIC_CAST_TO_MOST_DERIVED_CLASS(*i));
但据我所知,这种魔法并不存在,除了可能是一个充满了对 dynamic_cast 调用的讨厌的大 if 语句的形式。
那么是否有一些我不知道的运行时魔法?或者我应该实现和维护一个充满动态转换的大麻烦 if 语句?还是我应该接受它,放弃思考如何在 Ruby 中实现它,并让一点 Pesticide 进入我的水果头?
更新:与其做人为的只吃功能和杀虫剂,不如假设我只是不想把吃放在水果里,因为它没有意义。会吃自己的水果?普肖。相反,我需要一个带有 Eat 函数的 Eater 类,使用不同的代码来吃每种水果,以及一些默认代码,以防它是食客无法识别的水果:
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 的一堆调用。
但是,正如其中一个答案所暗示的那样,可能还有另一个聪明的解决方案。如果 Fruits 使用 MustPeel() 和 MustWash() 之类的函数暴露出对食客很重要的品质会怎样?然后你可以用一个 Eat() 函数来解决......
更新: Daniel Newby 指出,使用 Visitor 也可以解决所提出的问题……但这需要一些语义倒立(Fruit::use 或 Fruit::beEaten?)。
虽然我想接受几个答案,但我认为 psmears 的答案实际上是未来读者的最佳答案。感谢大家。