Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple inheritance pointer comparison

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)

like image 628
Lucretiel Avatar asked Jun 28 '12 21:06

Lucretiel


2 Answers

Well, no, it won't work.

I'm personally a big fan of learning-by-example, so here's one:

#include <iostream>

class Base1
{
public:
    Base1()
    {
        numberBase1 = 1;
    }

    int numberBase1;
};

class Base2
{
public:
    Base2()
    {
        numberBase2 = 2;
    }

    int numberBase2;
};

class Derived : public Base1, public Base2
{
public:
    Derived()
    {
        numberDerived = 3;
    }

    int numberDerived;
};

int main()
{
    Derived d;
    Base1 *b1 = &d;
    Base2 *b2 = &d;

    std::cout << "d: " << &d << ", b1: " << b1 << ", b2: " << b2 << ", d.numberDerived: " << &(d.numberDerived) << std::endl;

    return 0;
}

One run-through on my computer outputted this:

d: 0035F9FC, b1: 0035F9FC, b2: 0035FA00, d.numberDerived: 0035FA04

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:

Address:    Class:
0           Base1
4           Base2
8           Derived

.. 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 :) )

like image 63
cwap Avatar answered Sep 28 '22 08:09

cwap


Casting to Derived* before comparison is the right way to go.

There is a similar topic: C++ pointer multi-inheritance fun

like image 38
ialiashkevich Avatar answered Sep 28 '22 06:09

ialiashkevich