использование dynamic_cast для идентификации типа среды выполнения

при чтении Essential c++ главы 5.10 Идентификация типа во время выполнения я столкнулся с проблемой. Позвольте мне сначала представить небольшую предысторию. Есть базовый класс с именем num_sequence и класс Fibonacci, производный от num_sequence. В базовом классе есть виртуальная функция с именем gen_elems, а производный класс имеет собственное определение.

Следующее взято из книги.

Fibonacci fib;
num_sequence *ps = &fib;
ps->gen_elems(64);

Мы знаем, что будет вызван экземпляр Фибоначчи gen_elems(). Однако, хотя из этого теста мы знаем, что ps обращается к объекту класса Fibonacci, попытка вызвать экземпляр gen_elems() Fibonacci непосредственно через ps приводит к ошибке времени компиляции:

ps->Fibonacci::gen_elems(64); //выдает ошибку времени компиляции

ps не знает тип объекта, к которому он обращается, даже если мы и механизмы typeid и виртуальных функций знают.

Чтобы вызвать экземпляр Фибоначчи для gen_elems(), мы должны указать компилятору преобразовать ps в указатель типа Фибоначчи. Эту работу могут выполнять как static_cast, так и dynamic_cast.

Меня смущает жирная фраза. ps->gen_elems(64) фактически вызывает экземпляр Фибоначчи gen_elems(). Зачем нам использовать static_cast и dynamic_cast, чтобы преобразовать его в указатель типа Фибоначчи?


person Fihop    schedule 09.02.2013    source источник


Ответы (1)


В большинстве ситуаций вы просто вызываете виртуальный метод как обычно и позволяете полиморфизму делать свою работу, вызывая наиболее производную реализацию по мере необходимости. Автор пытается объяснить, что также можно напрямую вызвать конкретную реализацию виртуального метода, минуя полиморфизм.

Скажем, класс является производным от Fibonacci, чтобы снова переопределить gen_elems(), но вы не хотите вызывать это переопределение, вы хотите вызвать переопределение Fibonacci. Путем преобразования указателя num_sequence в указатель Fibonacci (или указатель потомка) во время компиляции он позволяет компилятору получить доступ к виртуальной таблице Fiboncci, чтобы он мог выдать код для прямого вызова Fibonacci::gen_elems() (если объект, на который указывает указатель во время выполнения, не на самом деле экземпляр Fibanocci или потомок, вы, скорее всего, сломаете/испортите свое приложение. Это не может быть проверено во время компиляции).

Например:

class num_sequence
{
public:
    virtual void gen_elems(int value)
    {
        std::cout << "num_sequence" << std::endl;
    }
};

class Fibonacci : public num_sequence
{
public:
  void gen_elems(int value)
  {
        std::cout << "Fibonacci" << std::endl;
  }
};

class SomethingElse : public Fibonacci
{
public:
  void gen_elems(int value)
  {
        std::cout << "SomethingElse" << std::endl;
  }
};

.

num_sequence ns;
Fibonacci fib;
SomethingElse se;
num_sequence *ps;

ps = &ns;
ps->gen_elems(64); // displays "num_sequence"

ps = &fib;
ps->gen_elems(64); // displays "Fibonacci"

ps = &se;
ps->gen_elems(64); // displays "SomethingElse"

ps->Fiboacci::gen_elems(64); // compiler error!

static_cast<Fibonacci*>(ps)->Fibonacci::gen_elems(64); // displays "Fibonacci"
static_cast<SomethingElse*>(ps)->Fibonacci::gen_elems(64); // displays "Fibonacci"

Fibonacci *pfib = dynamic_cast<Fibonacci*>(ps);
if (pfib != NULL)
{
    pfib->gen_elems(64); // displays "SomethingElse"
    pfib->Fibonacci::gen_elems(64); // displays "Fibonacci"
}
person Remy Lebeau    schedule 09.02.2013
comment
Проголосовал за, потому что Реми прав, но я хотел бы понизить голос Липпмана (автора Essential C++). Короткий ответ: если вы не новичок, вы можете захотеть сделать [какую-нибудь довольно загадочную вещь.] Он должен был знать, что не вводит этот материал здесь, и я удивлен, что он сделал это, поскольку в целом его материал великолепен. - person Chris Hartman; 09.02.2013