Нет, ваш пример не работает нормально.
Во-первых, вы можете использовать dynamic_cast
только для приведения между связанными типами классов , а не чем-то другим.
Во-вторых, даже если вызамените это dynamic_cast
на reinterpret_cast
или приведение в стиле C (что, я полагаю, вы имели в виду), тогда я получу следующий вывод:
c
c
Не совсем то, что вы хотели.
Почему это даже работает и не приводит к ужасным сбоям, потому что «безопасно» переходить назад и вперед между указателями на функции-члены, никакая информация не будет потеряна.
Почему он по-прежнему печатает что-то, потому что компилятор не видит ошибок с типами, но сборка не заботится о типах, она заботится только об адресах, поэтому она по-прежнему будет вызывать A::f
, потому что это указатель, который высохранено, независимо от типа.
Интересно, что это все еще работает, даже если вы не связаны классы (D
не наследует от C
), опять же, потому что сборка не заботится о типах.Изменение функций в A следующим образом:
void f( C& c ) { printf("f(C& c): %c\n",c.c); }
void g( D& d ) { printf("g(D& d): %c\n",d.d); }
приводит к следующему выводу:
f (C & c): c
f (C & c):d
«Как это работает? D
даже не имеет c
члена!».Ну, опять же из-за адресов.Обе переменные имеют одинаковое смещение от указателя this
, а именно +0
.Теперь давайте добавим еще один член в C
(упрощенный класс):
struct C{
C () : c('c') {}
int i; // mean
const char c;
};
И попробуйте еще раз, выведите:
f (C & c): c
f(C & C): 10
Да, мы идем.C::c
теперь находится по смещению +4
(+0
+ sizeof int
), а printf
считывает оттуда.В D
такого смещения нет, и printf
читает из неинициализированной памяти.С другой стороны, доступ к неинициализированной памяти неопределенное поведение .
Итак, чтобы окончательно прийти к выводу: нет, это небезопасно.:)