I have a class Derived
that inherits directly from two base classes, Base1
and Base2
. I'd like to know if it's safe, in general, to compare pointers to the base classes to determine if they are the same Derived
object:
Base1* p1;
Base2* p2;
/*
* Stuff happens here. p1 and p2 now point to valid objects of either their
* base type or Derived
*/
//assert(p1 == p2); //This is illegal
assert(p1 == static_cast<Base1*>(p2)); //Is this ok?
assert(static_cast<Derived*>(p1) == static_cast<Derived*>(p2)); //How about this?
The pointers are guaranteed to be valid, but not necessarily to point to a Derived
object. My guess is that this is probably fine, but I wanted to know if it was ok from a technical C++ perspective. I actually never do any operations on the pointers, I just want to know if they point to the same object.
EDIT: It seems to be safe if I can guarantee that p1
and p2
point to Derrived
objects. I basically want to know if it is safe if they don't- if one or both point to a base object, will the comparison necessarily fail? Again, I can guarantee the pointers are valid (i.e., p1
would never point at a Base2
object or vice versa)
Use
dynamic_cast
, and watch out for NULL.Casting to
Derived*
before comparison is the right way to go.There is a similar topic: C++ pointer multi-inheritance fun
None of them is a good solution. The first one will not compile, as you cannot compare pointers of unrelated types. The second one will not compile either (unless
Base1
andBase2
are related through inheritance) for the same reason: you cannotstatic_cast
to a pointer of an unrelated type.The third option is borderline. That is, it is not correct, but it will work in many cases (as long as inheritance is not virtual).
The proper way of comparing for identity would be using
dynamic_cast
to the derived type and checking for null:Well, no, it won't work.
I'm personally a big fan of learning-by-example, so here's one:
One run-through on my computer outputted this:
Soo.. If we define the address of d as 0, then b1 is 0, b2 is +4 and the number of d is +8. This is because an int on my machine is 4 byte long.
Basically, you have to look at the layout of how C++ internally represents a class:
.. So in total, instantiating a Derived class will allocate space for the base classes of the derived class, and finally make room for the derived object itself. Since we have 3 integers here, that'll be 12 bytes.
Now, what you're asking (unless I misunderstood something) is if you can compare the address of the different base class pointers to each other to see if they point to the same object, and the answer is no - Not directly at least, as in my example, b1 would point to 0035F9FC, while b2 would point to 0035FA00. In C++, this offsetting is all done at compile time.
You could probably do some magic with RIIA and sizeof() and determine how much of an offset b2 should have to be comparable to b1, but then you run into all kinds of other trouble like virtuals. In short, I would not recommend this approach.
A much better way would be to cast to Derived* like ialiashkevich said, however, that would impose a problem if your object was not an instance of Derived*.
(Disclaimer; I haven't used C++ in 3-4 years, so I might be a bit off my game. Be gentle :) )
The short answer is no, this is generally not a good idea.
NOTE: This is assuming you want custom equivalence for all of your classes, if you want to check if they are the same object, it is better to do
(Derived *)
.A much better solution would be to overload the
==
operator forBase1
,Base2
, andDerived
.Assuming
Base1
has 1 parameterparam1
for equality andBase2
has another parameterparam2
for equality:It appears to be invalid, based on this SO question: How is C++'s multiple inheritance implemented?
Basically, because of the way the objects are laid out in memory, a cast to either
Base1*
orBase2*
results in a mutation of the pointer that I can't arbitrarily reverse at runtime without adynamic_cast
, which I'd like to avoid. Thanks everyone!