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?
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 ?
CUser
by passing to the constructor aCItem
pointer.CParent_Right
, the compiler will cast theCItem
toCParent_Right
.CParent_Right
subobject. You cannot presume that it's aCItem
anymore; Not allCParent_Right
subobject are necessarilyCItems
!((CParent_Left*)data)->Foo();
will not yield a pointer to aCParent_Left
as you expect. It only takes the current address ofCParent_Right
and used it as if it where aCParent_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:
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: