dimanche 22 juillet 2018

dynamic_cast back to same object type fails with multiple inheritance and intermediate variable

Suppose a hierarchy with two unrelated polymorphic classes PCH and GME, a subclass PCH_GME : public GME, public PCH, and an object gme_pch of type PCH_GME*.

Why does the following sequence of casts of gme_pch "break" the cast back to the object's original type 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

whereas the following code does not break the casts:

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

Question: Isn't each pointer always pointing to the same object, and shouldn't then the result of the final cast back to the original type always be the same?


Just for making it easier to try things out, see the following code demonstrating variants of the above mentioned cast sequences; some working, some not:

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")<< 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")<< 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")<< 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")<< 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")<< std::endl;
}


int main() {

    castThatWorks();
    castThatWorks2();
    castThatDoesntWork();
    castThatDoesntWork2();
    castThatDoesntWork3();   
}

Aucun commentaire:

Enregistrer un commentaire