Предположим, у меня есть куча фруктов:яблоки, апельсины, и указатели на наиболее производного класса C++
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. Но это не вариант, потому что я не здравомыслящий человек. Кроме того, я не хочу, чтобы Pesticide * в заголовочном файле для моего класса фруктов.
К сожалению, то, что я хочу, чтобы иметь возможность делать дальше именно то, что функции-члены и динамическое связывание позволяют:
typedef list<Fruit*> Fruits;
Fruits fs;
...
for(Fruits::iterator i=fs.begin(), e=fs.end(); i!=e; ++i)
Eat(*i);
И очевидно, что проблема здесь заключается в том, что указатель мы переходим к Поесть) будет (Fruit *, а не Apple * или Orange *, поэтому ничего не будет съедено, и мы все будем очень голодны.
Так что я действительно хочу, чтобы иметь возможность делать вместо этого:
Eat(*i);
это:
Eat(MAGIC_CAST_TO_MOST_DERIVED_CLASS(*i));
Но моим ограниченным знанием, такой магии не существует, за исключением, возможно, в форма большого неприятного if-заявления, полного вызовов dynamic_cast.
Итак, есть ли магия времени выполнения, о которой я не знаю? Или я должен реализовать и поддерживать большой неприятный if-statement, полный dynamic_casts? Или я должен сосать его, перестать думать о том, как я буду реализовывать это в Ruby, и позволить небольшому пестициду попасть в мой заголовок фруктов?
Обновление: Вместо надуманного бита с голой функцией 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.
Однако, как следует из одного из ответов, может быть другое умное решение. Что, если Фрукты разоблачили качества, которые имели значение для едоков, с такими функциями, как MustPeel() и MustWash()? Затем вы могли бы пройти с помощью одной функции Eat() ...
Обновление: Daniel Newby указывает, что использование Visitor также решает проблему, как представлено ... но для этого требуется немного семантическая стойка (Fruit :: использование или фрукты :: beEaten?).
Хотя я хотел бы принять несколько ответов, я думаю, что ответ psmears на самом деле является лучшим для будущих читателей. Всем спасибо.
Возможно, вам стоит зайти в санитарный госпиталь и принять функцию члена? –
Хотя C++ может предоставить вам информацию о фактическом типе с помощью RTTI, я не знаю, каким образом заставить приведение к фактическому подтипу. – Jherico
Я не уверен, что вы хотите отправить несколько сообщений (http://en.wikipedia.org/wiki/Multiple_dispatch) или динамическую отправку (http://en.wikipedia.org/wiki/Dynamic_dispatch). – Juliano