Search code examples
c++pointersc++17visual-studio-2022data-integrity

Why does parent class pointer has wrong data when casted from child pointer to parent pointer?


Here is the class hierarchy that I have.

class EventA {/* adds itself to list */};
class EventB {/* adds itself to list */};
class EventC {/* adds itself to list */};
class EventD {/* adds itself to list */};

class EventE {/* adds itself to list */};
class EventF {/* adds itself to list */};
class EventG {/* adds itself to list */};
class EventH {/* adds itself to list */};

class Component : public EventA, public EventB, public EventC, public EventD {
    // Propeties and functions
    bool sentinel;
};

class CustomComponent : public Component {
    // Propeties and functions
};

class UserComponent : public CustomComponent, public EventE, public EventF, public EventG, public EventH {
    // Propeties and functions
};

I have a list of each type: EventE, EventF, EventG, EventH. They are added to the list through the constructor when I create the UserComponent object. The problem that I am running into is that when I iterate through the list of EventE, EventF, EventG and EventH, when I cast the pointer from either EventE, EventF, EventG or EventH to Component* some of the properties are not correct when interpreted as such a pointer. For example, sometimes my bool sentinel value will be true(216) when they are supposed to be false. My suspicion is that when I convert the pointer, it does not offset the memory to match the class hierarchy that I have laid out. In other words, I think it is not reading from correct memory address. Is anyone familiar with this issue? My problem is at system design level, so any off-topic solution, or any solution that has a different approach are welcome.

Response to Requested Minimal Example

This code is a minimal example of the problem that I am having. I provided it as requested in the comment section.

#include <iostream>

using namespace std;

class EventA {
    public:
        char a;

        EventA() {
            a = 'a';
        }
        ~EventA() {

        }
};
class EventB {
    public:
        char b;
        
        EventB() {
            b = 'b';
        }
        ~EventB() {

        }
};
class EventC {
    public:
        char c;

        EventC() {
            c = 'c';
        }
        ~EventC() {

        }
};
class EventD {
    public:
        char d;

        EventD() {
            d = 'd';
        }
        ~EventD() {

        }
};

class EventE {
    public:
        char e;
        
        EventE() {
            e = 'e';
        }
        ~EventE() {

        }
};
class EventF {
    public:
        char f;

        EventF() {
            f = 'f';
        }
        ~EventF() {

        }
};
class EventG {
    public:
        char g;

        EventG() {
            g = 'g';
        }
        ~EventG() {

        }
};
class EventH {
    public:
        char h;

        EventH() {
            h = 'h';
        }
        ~EventH() {

        }
};

class Component : public EventA, public EventB, public EventC, public EventD {
    public:
        int customValue;

        Component() {
            customValue = 1000;
        }
        ~Component() {

        }
};

class CustomComponent : public Component {
    public:
        int customComponentValue;

        CustomComponent() {
            customComponentValue = 2000;
        }
        ~CustomComponent() {

        }
};

class UserComponent : public CustomComponent, public EventE, public EventF, public EventG, public EventH {
    public:
        int userComponentValue;

        UserComponent() {
            userComponentValue = 3000;
        }
        ~UserComponent() {

        }
};

int main() {
    UserComponent userComponent = UserComponent();
    EventE* ptrEventE = (EventE*)&userComponent;
    Component* ptrComponent = (Component*)ptrEventE;
    cout << ptrComponent->customValue << endl;
}

If you go into debug mode and inspect the value of ptrComponent you will see exactly what I am talking about. Note that the casting that I made is exactly what is going on at different points of my system. That is why I did it the way I did.


Solution

  • The error is caught if you use static_cast instead of a C-style cast:

    a.cpp: In function 'int main()':
    a.cpp:134:31: error: invalid 'static_cast' from type 'EventE*' to type 'Component*'
      134 |     Component* ptrComponent = static_cast<Component*>(ptrEventE);
          |                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    

    EventE is not a base of Component, so the compiler does not know how the two are connected. In your case, they're connected by the common derived UserComponent, by there may be other common derived classes with different layouts as well, so it's of no help. static_cast throws an error in that case, C-style cast just does reinterpret_cast and assumes that no pointer modifications are needed.

    Two ways to fix: either make Component derive from EventE as well or cast through a known common derived class:

    int main() {
        UserComponent userComponent = UserComponent();
        EventE* ptrEventE = static_cast<EventE*>(&userComponent);
        Component* ptrComponent = static_cast<UserComponent*>(ptrEventE);  // UserComponent --> Component is implicit
        cout << ptrComponent->customValue << endl;
    }