Предположим, есть иерархия с двумя несвязанными полиморфными классами PCH
и GME
, подклассом PCH_GME : public GME, public PCH
и объектом gme_pch
типа PCH_GME*
.
Почему следующая последовательность приведения gme_pch
"ломает" приведение к исходному типу объекта GME_PCH*
:
GME_PCH *gme_pch = new GME_PCH();
GME *gme = gme_pch;
PCH *pch = (PCH*)gme;
GME_PCH *same_as_gme_pch = dynamic_cast<GME_PCH*>(pch);
// same_as_gme_pch is NULL
тогда как следующий код не нарушает приведения:
GME_PCH *gme_pch = new GME_PCH();
PCH *pch = gme_pch;
GME_PCH *same_as_gme_pch = dynamic_cast<GME_PCH*>(pch);
// address of same_as_gme_pch == gme_pch
Вопрос: Разве каждый указатель не всегда указывает на один и тот же объект, и не должен ли результат окончательного приведения обратно к исходному типу всегда быть одним и тем же?
РЕДАКТИРОВАТЬ: согласно ответам я добавил вывод адресов gme_pch
и pch
. И показывает, что отношение этих двух указателей в работающих вариантах отличается от тех, где оно не работает (т.е. в зависимости от того, пишется ли GME_PCE : public GME, public PCH
или GME_PCE : public PCH, public GME
, gme_pch
равно pch
в рабочем варианте и gme_pch
не равно в нерабочем вариантов и наоборот).
Просто для того, чтобы упростить опробование, см. следующий код, демонстрирующий варианты вышеупомянутых последовательностей приведения; некоторые рабочие, некоторые нет:
class PCH { // PrecachingHint
public:
virtual std::string getHint() const = 0;
};
class GME { // GenericModelElement
public:
virtual std::string getKey() const = 0;
};
class GME_PCH : public GME, public PCH {
public:
virtual std::string getHint() const { return "some hint"; }
virtual std::string getKey() const { return "some key"; }
};
void castThatWorks() {
GME_PCH *gme_pch = new GME_PCH();
PCH *pch = gme_pch;
GME_PCH *same_as_gme_pch = dynamic_cast<GME_PCH*>(pch);
std::cout << ((same_as_gme_pch == nullptr) ? "cast did not work." : "cast worked.")<< "gmepch:" << gme_pch << "; pch:" << pch << std::endl;
}
void castThatWorks2() {
GME_PCH *gme_pch = new GME_PCH();
GME *gme = gme_pch;
PCH *pch = dynamic_cast<PCH*>(gme);
GME_PCH *same_as_gme_pch = dynamic_cast<GME_PCH*>(pch);
std::cout << ((same_as_gme_pch == nullptr) ? "cast did not work." : "cast worked.")<< "gmepch:" << gme_pch << "; pch:" << pch << std::endl;
}
void castThatDoesntWork() {
GME_PCH *gme_pch = new GME_PCH();
GME *gme = gme_pch; // note: void* gme = gme_pch breaks the subsequent dynamic cast, too.
PCH *pch = (PCH*)gme;
GME_PCH *same_as_gme_pch = dynamic_cast<GME_PCH*>(pch);
std::cout << ((same_as_gme_pch == nullptr) ? "cast did not work." : "cast worked.")<< "gmepch:" << gme_pch << "; pch:" << pch << std::endl;
}
void castThatDoesntWork2() {
GME_PCH *gme_pch = new GME_PCH();
GME *gme = gme_pch;
PCH *pch = reinterpret_cast<PCH*>(gme);
GME_PCH *same_as_gme_pch = dynamic_cast<GME_PCH*>(pch);
std::cout << ((same_as_gme_pch == nullptr) ? "cast did not work." : "cast worked.")<< "gmepch:" << gme_pch << "; pch:" << pch << std::endl;
}
void castThatDoesntWork3() {
GME_PCH *gme_pch = new GME_PCH();
GME *gme = gme_pch;
PCH *pch = static_cast<PCH*>(static_cast<void*>(gme));
GME_PCH *same_as_gme_pch = dynamic_cast<GME_PCH*>(pch);
std::cout << ((same_as_gme_pch == nullptr) ? "cast did not work." : "cast worked.")<< "gmepch:" << gme_pch << "; pch:" << pch << std::endl;
}
int main() {
castThatWorks();
castThatWorks2();
castThatDoesntWork();
castThatDoesntWork2();
castThatDoesntWork3();
}
Выход:
cast worked.gmepch:0x100600030; pch:0x100600038
cast worked.gmepch:0x100600040; pch:0x100600048
cast did not work.gmepch:0x100600260; pch:0x100600260
cast did not work.gmepch:0x100202c30; pch:0x100202c30
cast did not work.gmepch:0x100600270; pch:0x100600270
reinterpret_cast
или эквивалент, тогда как в рабочих версиях используется только неявное преобразование иdynamic_cast
. - person M.M   schedule 23.07.2018