Re: [Solved] sequence of inheritance from virtual base class?
On 05/18/13, Ralf Fassel wrote:
* Richard Damon <Richard@Damon-Family.org>
| The order of inheritance should not matter for conforming code. A
| likely issue is code that assumes &Derived == &Base and does
| invalid casting on it.
Ok, solved it. The reason for the crash was that the base class
pointer was not derived directly from the Derived pointer, but there
was another void* involved:
// virtual base functions omitted for brevity
class Unrelated {};
class Base {};
class Derived : public Unrelated, public Base {};
Base *p1 = &Derived; // ok
// push into hashtable and retrieve it back via void*
hashtable_put("key", &Derived);
void *v = hastable_get("key");
Base *p2 = (Base*) v; // wrong, vtable offset incorrect
Base *p3 = (Derived*) v; // ok, vtable offset correct
The pointer returned by the hashtable_get() needs first to get cast
to the original class pushed into the hashtable, otherwise the
compiler can't calculate the correct vtable offset (obvious once you
grok it).
That's not quite right. It is actually the pointer value that is
wrong, not the calculation of the vtable offset.
Note that:
Derived d;
Derived* ptrDerived = &d;
Base* ptrBase = &d;
if (static_cast<void*> (ptrBase) != static_cast<void*> (ptrDerived))
std::cout << "Addresses are different";
else
std::cout << "Addresses are same";
On my system I have to introduce at least one virtual method in class
Unrelated in order to get "Addresses are different"
printed. Apparently gcc reorders the base classes in such a way that
any base classes without virtual methods are put at higher addresses
(see example below).
So my original observation that p2 and the original pointer were the
same was indeed the cause of the problem, they must *not* be the
same if Base is not the first base-class of Derived.
Not necessarily. The following code prints
Pointers to a are different.
Pointers to b are same.
on my system (I gave the classes some member variables so that we don't
get fooled by the empty-base-class-optimization):
class FirstBaseClass { int i;};
class SecondBaseClassWithoutVirtualMethods {int k;};
class SecondBaseClassWithVirtualMethods
{
int l;
virtual void foo () {}
};
class DerivedA : public FirstBaseClass,
public SecondBaseClassWithoutVirtualMethods {};
class DerivedB : public FirstBaseClass,
public SecondBaseClassWithVirtualMethods {};
int main ()
{
DerivedA a;
SecondBaseClassWithoutVirtualMethods* ptrSecondBaseA = &a;
if (static_cast<void*> (&a) == static_cast<void*> (ptrSecondBaseA))
std::cout << "Pointers to a are same.\n";
else
std::cout << "Pointers to a are different.\n";
DerivedB b;
SecondBaseClassWithVirtualMethods* ptrSecondBaseB = &b;
if (static_cast<void*> (&b) == static_cast<void*> (ptrSecondBaseB))
std::cout << "Pointers to b are same.\n";
else
std::cout << "Pointers to b are different.\n";
}
Another lesson learned. Had I looked up and posted the *real* code
in the first place instead of trying to simplify, I'm sure the error
would have been obvious (maybe even *before* posting :-)
{ Quoted signature removed -mod }
Well, I learned something too. I didn't know that the compiler is
allowed to lay out the base class members in a different order than
the one that is given in the class definition. So if one has to
achieve some memory layout, one has to take special care if one of the
base classes contains virtual methods.
Regards,
Stuart
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]