multiple inheritance casting between parents __vftable seems corrupted

179 Views Asked by At

I have following problem, let's assume User using RightParent Items, I also need added functionality, which I have in LeftParent,

some of left and right parent functionality is pure virtual

actual items passed to User inherits from rightParent (original contract) and leftParent (contract I added there)

it might look like this (compilable and same occurs on VS 2010)

left parent - added contract/functionality (i.e. I own the class)

#pragma once
struct CParent_Left
{
   CParent_Left(){}
   virtual void Foo() const =0;
   virtual ~CParent_Left(){}
};

right parent - original contract (in real this is Qt Object - i.e. I don't own the class)

#pragma once
struct CParent_Right
{
   CParent_Right(){}
   virtual void Bar() const =0;
   virtual ~CParent_Right(){}
};

the item (there exists multiple items, and all used within a program inherit from both parents)

#pragma once

#include "CParent_Left.h"
#include "CParent_Right.h"

class CItem : public CParent_Left, public CParent_Right
{
   public:
      CItem(){}
      virtual void Foo() const override {}
      virtual void Bar() const override {}
      virtual ~CItem(){}
};

the user (this one is normally inherited from Qt QObject), on its own (and Qt private inners) treat item as rightParent, added functionality implemented here sometimes need to treat item as leftParent

#pragma once

#include "CParent_Left.h"
struct CParent_Right;

class CUser
{
   CParent_Right * data; // normally obtained from parent Qt class

   public:
      CUser(CParent_Right * data) : data(data){}

      void CallFoo()
      {
         ((CParent_Left*)data)->Foo(); // calls Bar()
      }

      virtual ~CUser(){}
};

sample main

#include "CItem.h"
#include "CUser.h"

void main(int argc, char *argv[])
{
   auto item(new CItem());
   item->Foo(); // calls foo

   auto user(new CUser(item));
   user->CallFoo();
}

The problem is, when I hold the item, and call methods from either left or right parent, they are called correctly,

when I hold item as (pointer to) rightParent (as it is obtainable from Qt widget), and I know that it is some CItem (CItem1, CITem2...) which inherits from both (so CItem is CLeft_Parent and also CItem is CRight_Parent), when I try to cast it to leftParent, calls to leftParent methods are not working properly (in debugger, the jump to vftable lands somewhere as if it was corrupted)

Any Ideas?

1

There are 1 best solutions below

0
On BEST ANSWER

Multiple inheritance is something really great and powerful.

However, it requires extreme care when using casting. And unfortunately, what you do is undefined behaviour.

What is going wrong here ?

  • First you create a CUser by passing to the constructor a CItem pointer.
  • As the constructor is only defined for CParent_Right, the compiler will cast the CItem to CParent_Right.
  • From this moment, the pointer passed to the constructor is the pointer to the CParent_Right subobject. You cannot presume that it's a CItem anymore; Not all CParent_Right subobject are necessarily CItems !
  • In consequence, the casting ((CParent_Left*)data)->Foo(); will not yield a pointer to a CParent_Left as you expect. It only takes the current address of CParent_Right and used it as if it where a CParent_Left. This is undefined behaviour. With your compiler, it just takes the address of the first function in the vtable which causes this mismatch.

How to correct it ?

If your sure that the CParent_Right of the constructor was in fact a CItem, you are allowed to downcast to the CItem. This is legal and done properly. Then you can upcast CItem into CParent_Left and it will work as you'd expected:

    // ((CParent_Left*)data)->Foo(); // calls Bar() ===>  OUCH !!!!
    static_cast<CParent_Left*>(static_cast<CItem*>(data))->Foo(); // YES !!

Note that I used static_cast here because I'm sure of my object types, and I could use this kind of consturct even for non-polymorphic classes. However if in doubt, a dynamic_cast would be a safer alternative.

Object layout and casting

A small picture to illustrate the possible castings:

enter image description here