私がたくさんの果物を持っているとしましょう:
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); }
OK、待ってください。すぐそこに止まります。この時点で、正気の人なら誰でもEat()をFruitクラスの仮想メンバー関数にすることに注意してください。しかし、私は正気の人ではないので、それは選択肢ではありません。また、フルーツクラスのヘッダーファイルにその農薬*を含めたくありません。
悲しいことに、次にできることは、メンバー関数と動的バインディングで可能なことです。
typedef list<Fruit*> Fruits;
Fruits fs;
...
for(Fruits::iterator i=fs.begin(), e=fs.end(); i!=e; ++i)
Eat(*i);
そして明らかに、ここでの問題は、Eat()に渡すポインターがApple*やOrange*ではなくFruit*になるため、何も食べられず、私たち全員が非常に空腹になることです。
だから私がこれの代わりに本当にやりたいこと:
Eat(*i);
これは:
Eat(MAGIC_CAST_TO_MOST_DERIVED_CLASS(*i));
しかし、私の限られた知識では、そのような魔法は存在しません。ただし、dynamic_castへの呼び出しでいっぱいの大きな厄介なifステートメントの形式を除いては。
それで、私が気付いていない実行時の魔法はありますか?または、dynamic_castsでいっぱいの大きな厄介なifステートメントを実装して維持する必要がありますか?それとも、私はそれを吸い上げて、Rubyでこれをどのように実装するかについて考えるのをやめ、少量の農薬が私の果物のヘッダーに入るのを許可する必要がありますか?
更新:裸のEat関数とPesticideを使った工夫の代わりに、意味がないのでEatを果物に入れたくないと仮定します。自分で食べる方法を知っている果物?Pshaw。代わりに、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への一連の呼び出しのようです。
ただし、回答の1つが示唆しているように、別の賢い解決策があるかもしれません。Fruitsが、MustPeel()やMustWash()などの関数を使用して、食べる人にとって重要な品質を公開した場合はどうなりますか?次に、単一のEat()関数でうまくいくことができます...
更新: Daniel Newbyは、Visitorを使用すると、提示された問題も解決されると指摘しています...しかし、これにはセマンティックな逆立ちが必要です(Fruit::useまたはFruit::beEaten?)。
私はいくつかの答えを受け入れたいと思いますが、psmearsの答えは実際には将来の読者にとって最良のものだと思います。みんな、ありがとう。