Solved

Diamond-like multiple inheritance

Posted on 2000-03-20
17
211 Views
Last Modified: 2013-11-20
I have the following structure:
       CObject
        |    |
        ...  ...
      CFoo1  CFoo2
(both derived from CObject somehow)
        |     |
         CFoo3
(multiple inheritance from CFoo1 and CFoo2)
Is there possible to make it work, so that the result of IsKindOf(CFoo1) and IsKindOf(CFoo2) for CFoo3 would be true?
(I saw technoteNo 16, but I beleive there is a way around).
Thanks.


0
Comment
Question by:Lenochka
  • 7
  • 7
  • 2
  • +1
17 Comments
 
LVL 1

Expert Comment

by:tomkeane
ID: 2638122
CObject always wants to be the left-most base class (i.e. the first class listed in the class' definitioin)
In the example below, CFoo1 is the leftmost base class, and is OK, CFoo2 is not and will have problems.

class CFoo3 : public CFoo1, public CFoo2

If you want to use MI with MFC, don't derive everything from CObject.

If all you want is the  IsKindOf() functionality, you can get this with standard C++ RTTI, using dynamic_cast<>.

0
 
LVL 10

Accepted Solution

by:
RONSLOW earned 50 total points
ID: 2638290
Derive a class from CObject, say CObjectEx.  Derive all your objects from this class and only use pointer to this class (or clases derived from it). ie. don't use CObject directly in your code.

define

virtual BOOL CObjectEx::IsKindOf(const CRuntimeClass* pClass) const {
  return CObject::IsKindOf(pClass);
}

then for CFoo3 override this so it says

virtual BOOL CFoo3::IsKindOf(const CRuntimeClass* pClass) const {
  return CFoo1::IsKindOf(pClass) || CFoo2::IsKindOf(pClass);
}

The only problem with this solution is that if you have a
  CObject* p = ....
  if (p->IsKindOf(RUNTIME_CLASS(CFoo3))) ...
then, because IsKindOf is NOT virtual in CObject, this will call the standard CObject::IsKindOf and no the IsKindOf for the thing p is pointing to.

Thats why you should only use the CObjectEx.

 
0
 
LVL 12

Expert Comment

by:migel
ID: 2639134
Hi!
overriding non virtual method by virtual one is not good idea! (see any C++ book)
0
 
LVL 12

Expert Comment

by:migel
ID: 2639140
And other thinks:
diamond-like architecture can be achived by needed class aggregation and providing pointers to that members.for example:
      CObject
        |    |
        ...  ...
      CFoo1  CFoo2

class CFoo3:public CObject
{
private:
CFoo1 m_foo1;
CFoo2 m_foo2;
public:
operator CFoo1* (){return &m_foo1;}
operator CFoo2* (){return &m_foo2;}
}
0
 
LVL 10

Expert Comment

by:RONSLOW
ID: 2639998
migel .. not recommended if you are wrigint both the base and derived clases ..  but when you don't have control of the base class, that's all you can do.  Books are fine in theory .. but when it comes to 'badly' designed libraries .. you have to bend the 'rules' a little.

NOTE: your solution means a LOT of change to the program, or creating wrapper functions for each member of CFoo1 and CFoo2 ... not good.

eg. if CFoo1 has a member void f(), then you'd need to change calls from
  CFoo3 x; x.f();
to
  CFoo3 x; ((CFoo1*)x)->f();
of have to add to CFoo3 class
  void CFoo3:f() { m_foo1.f(); }
etc for all the memeber functions f

YUCK on both accounts.

And is you're really fussy, one can take my solution and change the name of the virtual function from 'IsKindOf' to 'IsKindOfEx' (say) and all is fine again re your objection to the function name.

0
 
LVL 12

Expert Comment

by:migel
ID: 2640205
hmm
RONSLOW>> I will not discuss with you. Bad programming technique can be total headache for further development. (so reading books is not only theory practice :-)
Diamond arch. is usefull when one using smart pointers and related things.
0
 
LVL 10

Expert Comment

by:RONSLOW
ID: 2642277
Yes .. I agree fully.  Bad techiques are a headache .. I criticse them myself .. especially when WHY they are bad (or even that there are caveats) is not explained.

Unfortunately, it is the bad techniques in MFC that are causing the problems here .. and sometimes it takes two wrongs to make a right.

MFC was not (in many ways) well designed in term of being extensible etc.  There are many things that one would long to have as virtual or public or whatever.  But they aren't and so one has to write one's code around what (in hindsight) is 'bad' design - and sometimes that means dong things we'd rather not and that are 'bad'.

Yes .. redefining a function as virtual in a derived class is (in general) bad design (from a pure point of view).

The reason it is bad is that is one calls the function from a pointer /reference to the base class, one will always get the base class function.  But it is not a problem if we never use pointers / references to the base class.  I mentioned that in my answer.

The advantage of the method I proposed is that it means minimal code changes and familiar looking code (still write with IsKindOf etc).

I'll grant you that if I named my virtual function IsKindOfEx that would better satisfy good technique guidelines.  One should also then define a private CObjectEx::IsKindOf so we cannot call it be accident.  Then code that previously called IsKindOf would need to change to call IsKindOfEx (hat is the price we pay with the name change).  Of course, there is STILL the danger of using CObject* pointers with IsKindOf .. so we'd still need to ensure we aren't using them.

A solution such as you proposed, while 'good' involves 'ugly' code and/or duplication of member function namesm and changes elsewhere in the code. etc.  

Not a nice design either.

I'm sure neither of us would WANT to write code this way if it weren't for the limitations / peculiarities / history of the MFC framework.

Of course, in an ideal world, we would simply use RTTI to do this, and multiple inheritance would work just fine.
0
 
LVL 12

Expert Comment

by:migel
ID: 2643542
:-)
Excuse me for my bad English. I haven`t enough lang. baggage to write so full explanation. I agree MFC is not OO fully compatible library :-(. but adding your own hacks to its clients is not good idea too.
of course adding nev virtual method IsKindOfEx.... will grant you correct results (and good technique also) but entire framework will not use it - only user classes know about new method.
0
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 
LVL 10

Expert Comment

by:RONSLOW
ID: 2646757
That's ok. migel.  Your English is certainy good enough .. better than some 'native' english speakers I know :-)

If one doesn't add some sort of 'hack' then the answer to the original question is 'you cannot do it'.  Neither your nor my solution will work with only CObject* pointers and IsKindOf etc.

The only way is to use some other methods for determining RTTI.

Of course, what REALLY should be done is that the application is (re)written in an OOD way so that there is no NEED to know the type of an object, because everything happens correctly through virtal functions etc.

Bascially, its an ugly situation with an ugly solution.

Well .. at least we've had a nice chat.  Lenochka hasn't said anything yet .. maybe his/her problem has gone away?


0
 
LVL 12

Expert Comment

by:migel
ID: 2648166
I agree IsKindOf bad replacement of the std RTTI mechanism, but it is historical rudiment.
Lenochka>> Where are you? Is our discussion helps you?
0
 
LVL 10

Expert Comment

by:RONSLOW
ID: 2648837
MFC CRuntimeClass, IsKindOf etc predates RTTI support.

Why they couldn't have made CObject::IsKindOf a virtual I don't know.  CObject already has several virtual functions, adding another wouldn't hurt.  Then our job would be so much easier (just override IsKindOf for the CFoo3 class).

Maybe if we petition MS enough they might make it so?  (NOT)
0
 

Author Comment

by:Lenochka
ID: 2650410
Guys,
The discussion is very helpful, but the situation I'm in is more complicated that I initially presented.
I have 3rd party library (Go++, if you have heard about it), so I could not change the code inside this library.
MY CFoo2 is my own class derived from some class from Go++ (which are all derived from CObject), CFoo3 is the class from Go++. IsKindOf are inside Go++ source code. And that means that all your suggestions at this moment (and I tried them all - believe me) didn't bring the solution.
Hope this information will help.
Thanx.
0
 
LVL 10

Expert Comment

by:RONSLOW
ID: 2651092
I htnk I must be missing something here.

CFoo2 is your class .. but you are saying CFoo3 (the multiply inherited one) is in Go++.  How could a class in Go++ be derived from one of your classes?  Is it a template class?

Or was that a typo and you meant thatCFoo1 is a Go++ class and CFoo2 is yours?
0
 

Author Comment

by:Lenochka
ID: 2651122
RONSLOW>> Yes, it was mistypo - CFoo1 is mine and CFoo2 is Go's and I'm trying to create CFoo3.
0
 
LVL 12

Expert Comment

by:migel
ID: 2652377
Hi!
IsKindOf uses virtual method: CRuntimeClass* CObject::      GetRuntimeClass() const;
so you can override it and return GO++ object (but in this case your own classes will know about CFoo3 nothing :-( )
0
 
LVL 10

Expert Comment

by:RONSLOW
ID: 2652682
GetRuntimeClass is indeed virtual and is declared by the DECLARE_DYNAMIC macro and written by the IMPLEMENT_DYNAMIC macro.

You can, of course, bypass this and override it yourself, but that won't help here as we want CFoo3 to be simultaneously of two different types - or more to the point, to have two different base-classes.

However, all the other functions associated with MFC runtime type checking are all non-virtual, so you cannot override them .. if only they were virtual one could override CObject::IsKindOf so that it returned true for both CFoo2 and CFoo3

The other place one might have been able to do something is in the CRuntimeClass::IsDerivedFrom.  If it were virtual one could similarly return true for both CFoo2 and CFoo3, but again MS in their wisdom made all its functions non-virtual too.

0
 
LVL 12

Expert Comment

by:migel
ID: 2652720
brrr. so deeper catacombs :-)
Lenochka >> may be hierarchy changing will be preffered way?
0

Featured Post

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
Introduction: Database storage, where is the exe actually on the disc? Playing a game selected randomly (how to generate random numbers).  Error trapping with try..catch to help the code run even if something goes wrong. Continuing from the seve…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now