Sign in to follow this  
rsegal

Multiple inheritance problem

Recommended Posts

Firstly if you reply with a post saying multiple inheritance is bad you will be cursed. I am well aware of the controversy and discussions over using MI. So this is essentially the class structure I've got... A E | | B D \ / C I call new on class C and store the pointers to that call as pointers of class E. This works well enough. However when I want to cast a pointer of class E which was originally created as a class C object to a pointer of class A I cannot do this directly. From what I can tell I need to first cast the class E pointer to class C then to class A. My problem is I won't know exact type of class C until runtime. Would there be a way to cast a pointer of class E to a pointer of class A without knowing the exact type of class C? Hope someone understands what I'm talking about.

Share this post


Link to post
Share on other sites
it sounds like you just messed up your object architecture. if you are using multiple inheritence you should never be needing to cast from one parent type to another without explicitly knowing the type of C. typically you'll have multiple inheritence only so specific sub-systems can concern themselves with the parent class objects, with no need to cross the hierarchy. so my suggestion would be to redesign things so you don't need to ever cast from a->e. if a and e always go together, they should be the same class or at least part of the same linear hierarchy.

otherwise, some bastardized form of dynamic_cast'ing to C. once you have C you automatically have a pointer to A.

-me

Share this post


Link to post
Share on other sites
There shouldn't be anything wrong, are sure your using the wright cast operator? you need the dynamic_cast operator to navigate type hierarchies, if you cast to a pointer it will indicate if it fails by returning zero, if you cast to a reference and it fails it will throw a bad_cast exception.

Also make sure your destructors are virtual aswell.

Share this post


Link to post
Share on other sites
Quote:
Original post by snk_kid
There shouldn't be anything wrong, are sure your using the wright cast operator? you need the dynamic_cast operator to navigate type hierarchies, if you cast to a pointer it will indicate if it fails by returning zero, if you cast to a reference and it fails it will throw a bad_cast exception.

Also make sure your destructors are virtual aswell.

I thought you had to have RTTI enabled to use dynamic_cast?

Share this post


Link to post
Share on other sites
You can always cast something to something else with old C-style casting.

A* theA = (A*)theEclass;

It's just a horribly unsafe habit to get into. Since you've designed yourself into a corner with the multiple inheritance you may just have to resort to such code thuggery.

Share this post


Link to post
Share on other sites
Quote:
Original post by pingz
You can always cast something to something else with old C-style casting.


A* theA = (A*)theEclass;


It's just a horribly unsafe habit to get into. Since you've designed yourself into a corner with the multiple inheritance you may just have to resort to such code thuggery.


Hehe code thugery, I like that. Yes I may have to do that. Thanks everyone for all the replies so far.

Share this post


Link to post
Share on other sites
Quote:
Original post by rsegal
Quote:
Original post by snk_kid
There shouldn't be anything wrong, are sure your using the wright cast operator? you need the dynamic_cast operator to navigate type hierarchies, if you cast to a pointer it will indicate if it fails by returning zero, if you cast to a reference and it fails it will throw a bad_cast exception.

Also make sure your destructors are virtual aswell.

I thought you had to have RTTI enabled to use dynamic_cast?


Yes if your using VC++ compilers, dynamic_cast is specifically for casting polymorphic types, not just between parent-child but also siblings aswell.

Don't use C style casts for this, it was never designed for polymorphic type navigation in mind.

Quote:
Original post by rsegal
Quote:
Original post by pingz
You can always cast something to something else with old C-style casting.


A* theA = (A*)theEclass;


It's just a horribly unsafe habit to get into. Since you've designed yourself into a corner with the multiple inheritance you may just have to resort to such code thuggery.


Hehe code thugery, I like that. Yes I may have to do that. Thanks everyone for all the replies so far.


There is absolutely no need for "code thugery"

Share this post


Link to post
Share on other sites
Quote:
Original post by Oluseyi
A and E are not related, so there is no rational conversion between them. Where's the problem here?

Redesign.


Yes the types A & E are not related types but E refers to an instance of type C which is related to A & E, take this example and see:


#include <iostream>

struct A { virtual ~A() {} };
struct E { virtual ~E() {} };
struct B : A {};
struct D : E {};
struct C : B, D {};

int main() {

E* f = new C;

A* g = dynamic_cast<A*>(f);

std::cout << "is null? " << std::boolalpha << (g == 0) << std::endl;

delete g;

return 0;
}

Share this post


Link to post
Share on other sites
Another alternative is to simply store both a pointer to A and E at the time you create C. Presumably you need the pointers for 2 unrelated systems, so at creation time pass C through to those 2 systems instead of trying to pass from 1 system to another (which really is screaming REDESIGN ME!!!).

EDIT: System may not be the best word, but you get the idea....

Share this post


Link to post
Share on other sites
I fully agree with Oluseyi and petewood, your design is flawed.

If you can't come up with a better way then give us more details of what the classes are, or post come code and we'll help you out furthur.

Share this post


Link to post
Share on other sites
While generally this type of problem is a symptom of bad design as Palidine, Oluseyi, petewood, and iMalc have pointed out, that is not always the case. The way you describe your situation, where you are saying you don't necessarily know that type C is the common child, actually leads me to believe that you may be using this concept properly.

What you are trying to do is exactly what COM (and other object models) do with a query concept. The reason this can be necessary is when you have an interface object that you use to interact indirectly with some unknown object type via virtual functions (usually across multiple executables IE abstractly interfacing with an object created in a DLL). The targetted object's implementation can change, but you still want to be able to interface with it with the same interface type. As well, if more functionality is developed, a new interface which exposes that functionality may also be created. This can quickly and easily be handled with multiple inheritance where the unknown object type inherits from both interface types -- now you can have a function which creates the object and returns a pointer to one base and a function that creates the object and returns a pointer to the other base.

However, lets say a library you have needs to interface with instances of the main object type via the old interface (since it was developed before the new interface), yet your program needs to interface with instances of the main object type with instances of the new interface type because you are using additional features of the object. Inside your program you wish to use the old library, meaning you have to pass interfaces to the common object type. So, you now have to be able to convert from one interface type to the other at a time other than the creation of the object. Since you don't know what the common child type is, you can't do two static_casts or two c-style casts. As well, even if both interfaces share a common base (IE COM's IUnknown) you can't use simple casts since the inheritance would either be non-virtual, in which case the pointer conversion wouldn't be correct (since each would have their own instance of the base, not a shared one), or the base would be virtual, in which case it is illegal to convert (since the pointer offset could be different depending on the other types which potentially virtually inherit from that type in the hierarchy). So, you are left with dynamic_cast.

Unfortunately, if the reason you don't know the common child type is because the common child is created in some separate executable as I explained in my previous example, then dynamic_cast isn't even a valid option (as most implementations of dynamic_cast will not work across executables since they work off of known type information at compile time). In this case, you are forced to use your own form of RTTI, most-likely similar to COM's QueryInterface which takes a GUID and provides a pointer to the desired interface if it is supported (only COM's QueryInterface is also meant to work with interfaces which may not even be types that are inherited from, particularly if they are aggregates).

So, again, your design isn't necessarily flawed. In fact, if it's something like the example I gave, then your design is actually very good.

As a recap, if you "don't know" the common child yet the parent types are in the same executable, use dynamic_cast. If you don't know the common child because it is created in another executable (IE a DLL that acts as a factory), then you'll have to use a QueryInterface type of concept or some other form of custom RTTI.

Whatever you do, do NOT use a reinterpet_cast or a c-style cast (which was unfortunately recommended and that you agreed on doing):

A* my_a_pointer = reinterpret_cast< A* >( my_e_pointer ) // don't do this

/* or */

A* my_a_pointer = (A*)my_e_pointer // don't do this either





The reason why you shouldn't do this is because while it will compile it is not guaranteed to work (and most-often will NOT work). What will happen is your compiler will just take the raw pointer value and copy it to the pointer of the other type. The problem with this is that when working with inheritance (particularly multiple inheritance without empty bases and/or virtual inheritance) a pointer conversion is often NOT just a direct copy of the address. Usually the pointer has to be offset (since both objects may not have exactly the same referential location in memory). In turn, doing a c-style cast or a reinterpret_cast will not give you a compile time error, however you will get undefined results at runtime since the pointer will now point to a location that isn't necessarily to the referential address of the desired object!

Hope that helped. If not, post more information about your situation and why you need this functionality and we'll probably be able to give you more specific solutions.

Edit: Typos, wording, and source tags

[Edited by - Polymorphic OOP on September 22, 2004 8:12:36 AM]

Share this post


Link to post
Share on other sites
See cross cast.

Quote:
rsegal
Firstly if you reply with a post saying multiple inheritance is bad you will be cursed.

Multiple inheritance is bad. But it's not MI that I oppose, it's using casts.

What is your situation? It's likely there is a better, safer, more flexible design just waiting to get out, even with MI.

Pete

Share this post


Link to post
Share on other sites
Quote:
Original post by petewood
Multiple inheritance is bad.

I disagree completely, not only with respect to situations like those I've mentioned, but more commonly also with non-public multiple inheritance (for instance, use with a non-copyable base type like boost's noncopyable, CRTP such as with singleton bases or things like boosts operator sublibrary, or other common concepts which are centered around having bases for reasons other than the classic object-oriented "is-a" concept).

Share this post


Link to post
Share on other sites
Quote:
Original post by Polymorphic OOP
Quote:
Original post by petewood
Multiple inheritance is bad.

I disagree completely, not only with respect to situations like those I've mentioned, but more commonly also with non-public multiple inheritance (for instance, use with a non-copyable base type like boost's noncopyable, CRTP such as with singleton bases or things like boosts operator sublibrary, or other common concepts which are centered around having bases for reasons other than the classic object-oriented "is-a" concept).


Sorry, I thought it was clear I was reacting to being told I would be cursed for having a view that he disagreed with.

As I said, it is possible to have a good design with MI. It's the casting which causes the problems.

If you agregate you can have a new object and wrap it in an old interface.

Share this post


Link to post
Share on other sites
Quote:
Original post by petewood
Sorry, I thought it was clear I was reacting to being told I would be cursed for having a view that he disagreed with.


Ha, okay, understood. That reaction has almost become a reflex for me. I just go crazy when someone claims that any programming concept is always bad.

Edit: misread -- elaborate on how you would suggest aggregation as a solution as here I don't immediately see why one would be able to come up with a less cumbersome solution with aggregation over multiple inheritance in this case (consider the example I provided as a case if you can).

Share this post


Link to post
Share on other sites

//OldInterface.h
struct OldInterfaceImpl;

struct OldInterface {
OldInterface(OldInterfaceImpl*);
void SomethingOld();
private:
OldInterfaceImpl* m_pImpl;
};




//OldInterfaceImpl.h
struct OldInterfaceImpl {
private:
//to customise OldInterface you override this function
virtual void DoSomethingOld() {}
};




struct NewInterfaceImpl;

struct NewInterface {
NewInterface(NewInterfaceImpl*);
void SomethingNew();
private:
NewInterfaceImpl* m_pImpl;
};




//WrapOldInterfaceRoundNewInterface.h
//This is where the delegation from the old interface to the new happens
struct WrapNewInterfaceWithOld : OldInterfaceImpl {
WrapNewInterfaceWithOld(NewInterface* object)
: m_NewInterface(object) {
}
void DoSomethingOld() {
m_NewInterface->SomethingNew(); //delegation
}
private:
NewInterface* m_NewInterface; //aggregation
};




OldInterface* GetAccessToNewObjectsUsingOldInterface() {
//program makes use of NewInterace internally
NewInterface* object = GetNewInterfaceObject();
OldInterfaceImpl* impl = new WrapNewInterfaceWithOld(object);
return new OldInterface(impl);
}

Share this post


Link to post
Share on other sites
Quote:
Original post by Polymorphic OOP
Quote:
Original post by petewood
Sorry, I thought it was clear I was reacting to being told I would be cursed for having a view that he disagreed with.


Ha, okay, understood. That reaction has almost become a reflex for me. I just go crazy when someone claims that any programming concept is always bad.


I shouldn't be surprised as humour doesn't always go across well in type. I think my wit is particularly badly suited. Or maybe it's just too funny.

Share this post


Link to post
Share on other sites
The thing is, not only is that a lot more code, it doesn't entirely solve the problem in its current form:

For instance, now you have your wrapped NewInterface in a child of the OldInterface. So you return it and have a pointer to the OldInterface. Now how do you convert the OldInterface pointer from outside of that function to the NewInterface (remember that externally you can potentially have no knowledge of those additional types, as rsegal has claimed)? This is a situation that very well can occur and is what is described in the original posters problem as well as in my suggested scenerio.

In addition to the increase in the amount of code and the fact that the problem isn't fully solved, you are also using more dynamically allocated objects than you'd use with the suggested multiple inheritance model, which would only have the dynamic allocation of the child object (assuming you'd even need that object dynamically allocated, which isn't necessarily the case either). What's more is, the multiple inheritance model is most-likely much easier to understand since all you are doing is instantiating a child and interfacing with it via its bases, just like with any other public inheritance. No extra object types are introduced. All you need to know is C++ and the fact the the conversion is theoretically possible (unless, of course, you are working across executables as I described in my previous reply).

So the multiple inheritance model can be implemented in less code, without the addition of new types, and uses built-in functionality of the language making it less-likely for you to make an error (since the bulk of the work is done automatically by the compiler and not by a human who can understandably make mistakes).

Share this post


Link to post
Share on other sites
Quote:
Original post by Polymorphic OOP
For instance, now you have your wrapped NewInterface in a child of the OldInterface. So you return it and have a pointer to the OldInterface. Now how do you convert the OldInterface pointer from outside of that function to the NewInterface (remember that externally you can potentially have no knowledge of those additional types, as rsegal has claimed)? This is a situation that very well can occur and is what is described in the original posters problem as well as in my suggested scenerio.

NewInterface and OldInterface are not related. If you want a NewInterface you can get one from the application that gave you the OldInterface.

Quote:

In addition to the increase in the amount of code and the fact that the problem isn't fully solved, you are also using more dynamically allocated objects than you'd use with the suggested multiple inheritance model, which would only have the dynamic allocation of the child object (assuming you'd even need that object dynamically allocated, which isn't necessarily the case either). What's more is, the multiple inheritance model is most-likely much easier to understand since all you are doing is instantiating a child and interfacing with it via its bases, just like with any other public inheritance. No extra object types are introduced. All you need to know is C++ and the fact the the conversion is theoretically possible (unless, of course, you are working across executables as I described in my previous reply).

So the multiple inheritance model can be implemented in less code, without the addition of new types, and uses built-in functionality of the language making it less-likely for you to make an error (since the bulk of the work is done automatically by the compiler and not by a human who can understandably make mistakes).


Let's look at it another way. What you suggest is to make it easier to add new functionality as the inheritance will look after the support of old interfaces. Imagine you've got more than two versions. Imagine you have OldInterface, NewInterface, BrandNewInterface, LatestInterface, NextBigThingInterface, etc. Using it in this way will mean you end up with a class inheriting from all previous versions. This doesn't look good to me. It is difficult and error prone to understand what code is implementing what functionality for what version of the interface.

With my way of doing it ("he said d-oing"), your dependency upon old and legacy code is taken care of in one place, through a delegating class. Your new code really is new code. If you want to reuse some of the older code you can contain an object and use its functionality instead of inheriting it.

If it's not possible to use the new class with the limited old interface it won't be possible to write the delegating wrapper. I feel this better expresses the development of an interface. With your design it would be possible to get a handle on an object but not be able to meaningfully use it.

Share this post


Link to post
Share on other sites
Quote:
Original post by petewood
NewInterface and OldInterface are not related. If you want a NewInterface you can get one from the application that gave you the OldInterface.


But they are related in the sense that they both can interface with the same object, they can both be represented in the same hierarchy, and in different areas of a program you may need to interface with the same object via both (or multiple) different interfaces (this is one of the reasons why Java allows multiple inheritance with interfaces). This exact concept is directly representable with multiple inheritance with the types being sister types. The problem is that you say you can just get a new interface from the place you got the old interface, but this isn't always a possibility (not to mention the fact it's an unecessary hassle).

For instance, just because you get a pointer to an interface from one location, that doesn't mean it's the place it was created, as well, what if you were passed the pointer via function arguments -- you have no idea who passed it to you. You would always have to couple the interface pointer with information about who created it and be able to convert to that type (and potentially other interface types) via that information, otherwise you wouldn't have a way to make the conversion (since you aren't guaranteed that anyone else knows how).

The further you go with the aggregation concept in this situation, the harder you make it on yourself. In the end, you're just killing yourself making large amounts of additional functions and types just to emulate something that you can already do with built-in functionality of the language.

What exactly do you think is so bad about the pointer casting? In places where it actually makes sense to use it, such as here, it provides pretty much the most concise solution -- it certainly beats most of the alternatives, with one of the only other viable solutions being the Query concept (which often internally uses multiple inheritance anyways).

Quote:
Original post by petewood
Let's look at it another way. What you suggest is to make it easier to add new functionality as the inheritance will look after the support of old interfaces. Imagine you've got more than two versions. Imagine you have OldInterface, NewInterface, BrandNewInterface, LatestInterface, NextBigThingInterface, etc. Using it in this way will mean you end up with a class inheriting from all previous versions. This doesn't look good to me. It is difficult and error prone to understand what code is implementing what functionality for what version of the interface.


Are you saying that simply multiply inheriting from them is more difficult to understand and more error prone than using your solution (which again, still needs more work in order to match the capabilities of cross-casting). Multiple inheritance takes one line of code, no extra functions, and any complex work is done automatically by the compiler. To make the object and have it convertible to the other interfaces it's one line of code. To actually perform the conversion it's one line of code. There is a lot more that can go wrong by not using multiple inheritance since now you are forced to use your own rolled techniques and a lot of uneeded extra code. Even moreso, the multiple inheritance version is probably going to result in faster conversion. The only potential problem is that when you start getting into a very large amount of interfaces then you can bloat the size of the object due to the fact that each base will need its own vtable pointer, and it will force longer construction due to vtable pointer initialization. That's when it might be wise to consider more dynamic forms of interface "conversion" such as, again, via a Query mechanism.

Quote:
Original post by petewood
If you want to reuse some of the older code you can contain an object and use its functionality instead of inheriting it.


But you have yet to answer why you feel encapsulation makes more sense here over inheritance. You keep saying it as if encapsulation is always a better alternative to inheritance, which isn't true. In fact, this situation is a prime example for when to use inheritance over encapsulation. And again, in the current state of your solution you still aren't providing all of the power that multiple inheritance with cross-casting can provide, nor can you directly perform the conversion that was required in the original post (be able to convert between the types with no further knowledge of things such as who created it).

Quote:
Original post by petewood
With your design it would be possible to get a handle on an object but not be able to meaningfully use it.


How so? This is never the case -- if the conversion can't take place it won't give you a meaningless handle, your attempt at conversion will just fail. Your dynamic_cast will return 0, or throw an exception, or your Query function will not be successful. You'll never be put in a place where you have a handle to an object you can't meaningfully use.

I know I keep repeating myself by saying "like QueryInterface with COM" but I'm doing so with reason. The Query concept provides everything you need very simply, leaving you open for use of multiple inheritance or other methods of conversion. Querying in conjunction with multiple inheritance is the quickest, easiest, and probably fastest implementation you can get that satisfies all of the requirements that are given, and in practical use, it works across multiple executables.

Share this post


Link to post
Share on other sites
Quote:
Original post by Polymorphic OOP
I know I keep repeating myself by saying "like QueryInterface with COM" but I'm doing so with reason. The Query concept provides everything you need very simply, leaving you open for use of multiple inheritance or other methods of conversion. Querying in conjunction with multiple inheritance is the quickest, easiest, and probably fastest implementation you can get that satisfies all of the requirements that are given, and in practical use, it works across multiple executables.

OK, I'm having to get used to COM and QueryInterface at work now so I can see where you're coming from.
However this kind of thing isn't what I'd expect to be used in games. It just has that "sledgehammer to hit in a nail" feeling about it. Who knows, maybe it is a good idea.

One question though... Do you, or have you ever worked for Microsoft?

Share this post


Link to post
Share on other sites
I'm trying to work out how to reply. I don't want to cut your post up and reply piecemeal as it probably won't make a coherent answer. So I'll focus on one thing you've said. This seems to sum up everything I have a problem with:

Quote:
Original post by Polymorphic OOP
Multiple inheritance takes one line of code, no extra functions, and any complex work is done automatically by the compiler. To make the object and have it convertible to the other interfaces it's one line of code. To actually perform the conversion it's one line of code.


This is unlikely to be true.

Let's take two different cases:
One is where the classes that are being inherited are pure abstract interfaces - i.e. they have no data members and no implementation. You inherit so that you have that interface.

The other is where the class you are inheriting have some implementation and data. You are inheriting to reuse the implementation and to possibly customise it through overriding virtual functions and also to have the interface.

If you take the first case, simply adding one line of code to inherit from another base class will make it possible to convert between two different base class pointers using dynamic_cast. But you still have to provide an implementation. So it isn't as simple as adding one line of code.

If you take the second case, simply adding one line of code to inherit the interface and the implementation will mean you don't have to provide any implementation and you can convert between two different base pointers using dynamic_cast. However, if you haven't implemented anything yourself then the new class you have with two bases with implementation code don't actually interact in any way. It is as though you have two classes stuck together but they actually keep themselves to themselves, all their data is their own, they don't use each others functions etc. So if you are using the interface provided by one base class you will only be using the functions and data from that base class implementation. If you use a pointer to the other base class, again you will only use those functions and data.

If you want to actually have it that you are benefiting from inheriting from both, you need to have functions in the derived class which make use of functions from both bases. Otherwise the class is two classes lumped together.

I hope you can see that it's not as simple as just deriving from another base. The extra code I have had to type is little more than the extra code you would have to type to actually make use of the inheritance in a meaningful way.

Pete

Share this post


Link to post
Share on other sites
Thanks for the responses everyone. It looks like dynamic cast is working out for me. It seems to handle the crosscasting of my pointers quite nicely.

I've been reading up on dynamic cast and and if I've got it right the dynamic_cast cast allows you to downcast a pointer which was created as a derived class but stored as a base class back to the dervied class type safely. I'm meaning downcast to cast from a base class to a dervied class, perhaps that's incorrect terminology. I'm sure someone will correct me if I am mistaken.

So in my example off of my first post where I have the inhertiance structure

A E
| |
B D
\ /
C

I create objects of class C, store them as pointers of class D and then dynamic cast them later on to pointers of class B. This seems to work for me and does it make sense to me that an instance of class C should be able to be cast to objects of classes A, B, D or E.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this