• Advertisement
Sign in to follow this  

C++ inheritance and type casting problem

This topic is 3832 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have 4 classes: Component, Container, Panel and Form. Here is the basic layout:
class Component {
   Component *Parent;
}

class Container {
    vector<Component *> components;
}

class Panel : public Container, Component {

}

class Form : public Container, Component {

}

Now Component has a function BringToFront() that looks like this:
void Component::BringToFront() {
  Container *container = (Container *)Parent;
  container->RemoveControl(this);
  container->InsertAt(0, this);
}

But for some reason the casting of the Parent pointer to a Container is not working correctly. When I try to call any method the code crashes with no real relevant information. I've had to write a workaround that looks like this:
void Component::BringToFront() {
  Container *container;

  if (Parent->ComponentType() == FORM)
     container = (Container *)((Form *)Parent);
  else
     container = (Container *)((Panel *)Parent);  

  container->RemoveControl(this);
  container->InsertAt(0, this);
}

And now everything works. But now my Component class but know that the Form and Panel classes exist, ect... Is there an obvious reason why my first code doesn't work?

Share this post


Link to post
Share on other sites
Advertisement
A Component is not a Container. These types are not related in anything, except that they are both bases of other classes. Casting between them does not take that into consideration and that's why you won't retrieve the other base unless you downcast the object's pointer. When you have multiple bases, there are different addresses being used for the this pointer of each base class (most probably).

Share this post


Link to post
Share on other sites
Think a little bit about what happens when you downcast or upcast. The parent class data is actually stored together in the child class. When you cast a pointer from one to the other, the pointer is modified to point to the beginning of the memory for that class.

Example:


class A {
public:
int a;
};

class B {
public:
int b;
};

class C : public A, public B {
public:
int c;
};

class D : public A, public B {
public:
int d;
};

...

C c;
D d;
A* a = &c;

printf("&c = %08X, (A*)&c = %08X (%d), (B*)&c = %08X (%d)\n", &c, (A*)&c, (int)((A*)&c) - (int)&c, (B*)&c, (int)((B*)&c) - (int)&c );
printf("&d = %08X, (A*)&d = %08X (%d), (B*)&d = %08X (%d)\n", &d, (A*)&d, (int)((A*)&d) - (int)&d, (B*)&d, (int)((B*)&d) - (int)&d );
printf(" a = %08X, (B*) a = %08X (%d)\n", a, (B*)a, (int)((B*)a) - (int)a );

Results:
&c = 0012FEC0, (A*)&c = 0012FEC0 (0), (B*)&c = 0012FEC4 (4)
&d = 0012FECC, (A*)&d = 0012FECC (0), (B*)&d = 0012FED0 (4)
a = 0012FEC0, (B*) a = 0012FEC0 (0)




You can see that casting from C to B, changes the pointer to point to the data for B. However if you cast between unrelated classes A and B, this change does not happen.

Your fix causes the cast to go through the child class, and tells the compiler how to convert the pointer.

Edit:
Since container is a component, why not just derive container from component?

Share this post


Link to post
Share on other sites
This is an example of why C-style casts should be avoided. Component::Parent is a Component and not a Container. If you used C++-style casts, the compiler will tell you so (in a cryptic way, or course). Furthermore, once you figure out how to do the casting, the C++-style casts will handle multiple inheritance correctly (C-style casts won't).

Consider changing the design to get rid of the multiple inheritance -- a Component contains a Container, or Container is derived from Component and/or Component::Parent is pointer to a Container.

Share this post


Link to post
Share on other sites
Quote:
Original post by JohnBolton
This is an example of why C-style casts should be avoided. Component::Parent is a Component and not a Container. If you used C++-style casts, the compiler will tell you so (in a cryptic way, or course). Furthermore, once you figure out how to do the casting, the C++-style casts will handle multiple inheritance correctly (C-style casts won't).

Consider changing the design to get rid of the multiple inheritance -- a Component contains a Container, or Container is derived from Component and/or Component::Parent is pointer to a Container.


I think I will extend Container to inherit from Component. It makes more sense this way, since a Panel is a 'type' of Container which is a 'Component'.

Does anybody else think this is wrong? Should it be designed differently?

Share this post


Link to post
Share on other sites
Simplify a little.


class Component {
Component* Parent;
vector<Component*> children;
// If any particular component isn't a container, just leave the vector empty.
};

class Panel : public Component {
};

class Form : public Component {
};


(But you really should use smart pointers instead of raw ones.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement