Предположим, у меня есть фрукты:
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));
Но, насколько я знаю, такая магия несуществует, за исключением, возможно, в форме большого противного оператора if, полного вызовов динамической трансляции.
Так есть ли какая-то магия времени выполнения, о которой я не знаю?Или я должен реализовать и поддерживать большое противное выражение if, полное dynamic_casts?Или я должен смириться с этим, перестать думать о том, как бы реализовать это в Ruby, и позволить небольшому количеству пестицидов проникнуть в мою колбу с фруктами?
Обновление: Вместо надуманногонемного с голыми функциями Eat и пестицидами, предположим, что вместо этого я просто не хочу употреблять Eat в фрукты, потому что это не имеет смысла.Фрукт, который умеет себя есть?Тьфу.Вместо этого мне нужен класс Eater с функцией Eat, с разным кодом для употребления каждого вида фруктов и кодом по умолчанию на тот случай, если этот фрукт не распознается едоком:
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 () ...
Обновление: Даниэль Ньюби отмечает, что использование Visitor также решает проблему, как показано ... но это требует немногосемантической стойки на голове (Fruit :: use или Fruit :: beEaten?).
Хотя я хотел бы принять несколько ответов, я думаю, что ответ psmears на самом деле лучший для будущих читателей.Спасибо всем.