Mat*_*owe 5 c++ polymorphism inheritance multiple-dispatch visitor
假设我有一堆水果:
class Fruit { ... };
class Apple : public Fruit { ... };
class Orange: public Fruit { ... };
Run Code Online (Sandbox Code Playgroud)
以及在所述水果上运行的一些多态函数:
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); }
Run Code Online (Sandbox Code Playgroud)
好,等一下.停在那儿.请注意,任何理智的人都会使Eat()成为Fruit类的虚拟成员函数.但这不是一种选择,因为我不是一个理智的人.另外,我不想在我的水果类的头文件中使用Pesticide*.
遗憾的是,我希望接下来要做的就是成员函数和动态绑定允许的内容:
typedef list<Fruit*> Fruits;
Fruits fs;
...
for(Fruits::iterator i=fs.begin(), e=fs.end(); i!=e; ++i)
Eat(*i);
Run Code Online (Sandbox Code Playgroud)
显然,这里的问题是我们传递给Eat()的指针将是Fruit*,而不是Apple*或Orange*,因此什么都不会被吃掉,我们都会非常饥饿.
所以我真的希望能够做到而不是这样:
Eat(*i);
Run Code Online (Sandbox Code Playgroud)
这是:
Eat(MAGIC_CAST_TO_MOST_DERIVED_CLASS(*i));
Run Code Online (Sandbox Code Playgroud)
但是根据我有限的知识,这种魔法并不存在,除非可能是一个充满调用dynamic_cast的大讨厌if语句.
那么有一些我不知道的运行时魔法吗?或者我应该实现并维护一个充满dynamic_casts的令人讨厌的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!
Run Code Online (Sandbox Code Playgroud)
但同样,这不起作用,C++中的直接解决方案似乎是对dynamic_cast的一堆调用.
然而,正如其中一个答案所暗示的那样,可能还有另一个聪明的解决方案.如果Fruit通过MustPeel()和MustWash()等函数暴露出对食用者至关重要的品质,该怎么办?那么你可以通过一个Eat()函数来完成......
更新: Daniel Newby指出使用Visitor也解决了所提出的问题......但这需要一些语义倒立(Fruit :: use或Fruit :: beEaten?).
虽然我想接受几个答案,但我认为psmears的答案实际上是未来读者最好的答案.感谢大家.
你需要重新设计.也就是说,做你似乎想要避免的一切(出于什么原因,谁知道.)
多态行为需要多态函数.这意味着一个virtual
功能.(或者你的阶梯dynamic_cast
,这完全违背了目的......)
// fruit.h
class Pesticide; // you don't need a complete type
struct Fruit
{
virtual void Eat(Pesticide*) = 0;
};
// apple.h
class Apple : public Fruit
{
void Eat(Pesticide* p) { ... }
};
// orange.h
class Orange : public Fruit
{
void Eat(Pesticide* p) { ... }
};
Run Code Online (Sandbox Code Playgroud)
如果你还想要一个免费的功能*:
void Eat(Fruit* f, Pesticide* p) { f->Eat(p); }
Run Code Online (Sandbox Code Playgroud)
*请注意,您的帖子已经表明设计不佳; 即第一个Eat
功能:
void Eat(Fruit* f, Pesticide* p) { }
Run Code Online (Sandbox Code Playgroud)
何时对水果无所谓等于吃水果?纯虚函数是一个更好的界面选择.
当出现这样的问题时,最好弄清楚为什么你想要做出特定的决定 - 例如,为什么你不希望水果类了解农药?
我确信这样做有一个很好的理由 - 但表达这个理由将有助于你在头脑中明确你的目标是什么 - 这通常会为构建计划的可能角度提供新的启示。
例如,您最终可能会添加新的虚拟方法“IsEdible”和“PrepareForEating”。然后,您可以为每种水果实现这些,并实现一种适用于所有水果的通用 Eat 方法 - 并且也摄入讨厌的杀虫剂 - 所有这些 Fruit 类都对此一无所知。
当然,根据您的具体目标,这可能完全不合适 - 这就是为什么您必须在自己的头脑中澄清这个示例:-)