• Advertisement

Archived

This topic is now archived and is closed to further replies.

C++: Multiple Inheritance problem

This topic is 5787 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I''m playing around a bit with the windows API, and have created some helper-classes for dealing with the creation of windows. Basically I have some classes looking like this:
  
class WinMaker {
public:
    WinMaker (WinClass* winClass) : /* stuff */ { }
    void AddStyle (DWORD s) { style |= s; }
    /* Other methods */
    void Create();

    virtual void Paint (HDC hDC) { }
protected:
    /* Member-data */
};

class BitmappedWinMaker : public WinMaker {
public:
    /* BitmappedWinMaker is a windows-class that supports
       blitting bitmaps to the window */
    BitmappedWinMaker (WinClass* winClass) : WinMaker(winClass), /* other stuff */ { }
    
    void LoadBitmapFromFile (const char* szFilename);
    void Paint (HDC hDC) {
        BitBlt (hDC, 0, 0, width, height, dcSkin, 0, 0, SRCCOPY);
    }
protected:
    HBITMAP hBitmap;
    HDC dcSkin;
};

class RegionedWinMaker : public WinMaker {
public:
    /* RegionedWinMaker has support for creating window-regions
       for oddly shaped windows */
    RegionedWinMaker (WinClass* winClass) : WinMaker(winClass), /* other stuff */ { }

    void LoadRegionFromFile (const char* szFilename);

protected:
    HRGN hRegion;
};
  
So far, so good. This works as they are supposed to, so I decided to create a class that had both regions and bitmaps. So, I figured I''d do this:
  
class SkinnedWinMaker : public BitmappedWinMaker, public RegionedWinMaker {
public:
    SkinnedWinMaker (WinClass* winClass) : WinMaker(winClass) { }
};
  
Which gave me a whole bunch of error-messages. Most of them dealing with the fact that the methods declared in WinMaker were now ambiguous, and that the constructor for SkinnedWinMaker was faulty. I managed to fix the constructor-problem. But I completely fail to see how the WinMaker methods are ambigous in SkinnedWinMaker, as they are non-virtual. Anyways, in order to get somewhere I made them virtual in WinMaker, and explicitly overrode them in SkinnedWinmaker, which simply called BitmappedWinMaker or RegionedWinMaker''s similar methods, which got rid of all the error-messages, but was a serious hassle, and doesn''t seem right, somehow. So, the question is, why does the compiler (VC++ 6.0, btw) feel that non-virtual methods in WinMaker are amiguated more than 2 subclasses down? Does anyone know of any other useful way of getting the class-hierarchy done properly? Oh, and also: After I got rid of all the damned compiler-errors I tried the class (SkinnedWinMaker), but although it was able to blit the bitmap on allright, it was unable to use regions. -Neophyte -----BEGIN GEEK CODE BLOCK----- Version: 3.1 GED d- s:+ a- C++$ UL++ P++ L++ E W+ N+ o K? w !O M--(+) V-- PS+ PE Y+ PGP t-- 5++ X+ R(+) rv+(++) b+++ DI+ D(+) G e+>++ h r--> y+ ----- END GEEK CODE BLOCK----- geekcode.com

Share this post


Link to post
Share on other sites
Advertisement
This is the classic diamond inheritance heirarchy.

e.g.

B
/ \
D1 D2
\ /
D3


You need to use virtual base classes

try the following:


  
class SkinnedWinMaker : virtual public BitmappedWinMaker, virtual public RegionedWinMaker {


Note that when you do this only the most derived class can explicitly call a ctor of the virtual base class - in this case WinMaker

So you need to make sure that BitmappedWinMaker''s ctor does not directly invoke a WinMaker ctor in its initialization list


Share this post


Link to post
Share on other sites
quote:
Original post by Neophyte
So, the question is, why does the compiler (VC++ 6.0, btw) feel that non-virtual methods in WinMaker are amiguated more than 2 subclasses down?

How does the compiler determine which version of the WinMaker methods to call?

[ GDNet Start Here | GDNet Search Tool | GDNet FAQ | MS RTFM [MSDN] | SGI STL Docs | Google! | Asking Smart Questions | Internet Acronyms ]
Thanks to Kylotan for the idea!

Share this post


Link to post
Share on other sites
Hi,

First, I think you should declare methods of the base class virtual, but, that''s only my preference.

Second, in SkinnedWinMaker, methods from WinMaker becomes ambiguous because in fact, from the compiler point of view, it has to choose between methods inherited through BitmappedWinMaker and the ones through RegionedWinMaker. That''s why you had to override.

BTW, I advise you that RegionedWinMaker and BitmappedWinMaker should inherit virtually from WinMaker so that if there are datafields in WinMaker, it will be shared. Actually, if there was any data field, you would have the same ambiguity like methods. So :


class BitmappedWinMaker : virtual public WinMaker {...};
class RegionedWinMaker : virtual public WinMaker {...};



Now, you may reconsider how you designed your class hierarchy. You may use for instance, interface classes "a la java". I explain :

All your classes are basically "WinMaker" object, but some has special ability : BitmappedWinMaker can load a bitmap and RegionedWinMaker can load a region

So, you define your base class like you did, then you define 2 "interface classes"


  
class Bitmappable {
virtual void LoadBitmapFromFile (const char* szFilename) = 0 ;
} ;

class Regionable {
virtual void LoadRegionFromFile (const char* szFilename) = 0 ;
} ;


then, here are your 3 subclasses


  
class BitmappedWinMaker : virtual public WinMaker, virtual public Bitmappable {...};
class RegionedWinMaker : virtual public WinMaker, virtual public Regionable {...};
class SkinnedWinMaker : virtual public WinMaker, virtual public Regionable, virtual public Bitmappable {...};


Of course, don''t forget to declare and implements inherited methods from interfaces classes


----
David Sporn AKA Sporniket

Share this post


Link to post
Share on other sites
SteveC: That didn''t solve the problem of ambiguity.

Oluseyi: I would think that since WinMaker''s methods are non-virtual that SkinnedWinMaker would only have 1 set of those methods, thus making them unambiguous. Possibly I''m a bit confused on how multiple inheritance work. I would''ve thought that SkinnedWinMaker would have one pointer to the vtable for BitmappedWinMaker, one for SkinnedWinMaker and one for WinMaker (thus making WinMaker''s methods unambiguous).
But it would seem that it has in effect two pointers to WinMakers vtable, one via SkinnedWinmaker and one via BitmappedWinMaker. In that case it''s understandable that there is an ambiuity with WinMaker''s methods. Am I correct in thinking that, or am I way off again?

-Neophyte

-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GED d- s:+ a- C++$ UL++ P++ L++ E W+ N+ o K? w !O M--(+) V-- PS+
PE Y+ PGP t-- 5++ X+ R(+) rv+(++) b+++ DI+ D(+) G e+>++ h r--> y+
----- END GEEK CODE BLOCK-----
geekcode.com

Share this post


Link to post
Share on other sites
quote:
Original post by Neophyte
I would think that since WinMaker''s methods are non-virtual that SkinnedWinMaker would only have 1 set of those methods, thus making them unambiguous.

Non-virtual doesn''t mean that they won''t be inherited by derived classes. The virtual keyword is used to specify that a function will act polymorphically. Everything is inherited whether you specify virtual or not.


[C++ FAQ Lite | ACCU | Boost | Learning C++]

Share this post


Link to post
Share on other sites
- Quick reply -
Maybe something like aggregation or adapters instead of inheritence may help you.
Don''t forget Google to lookup there words.


----------------
Blaster
Computer game programmer and part time human being
Strategy First - http://www.strategyfirst.com
BlasterSoft - http://www.blastersoft.com

Share this post


Link to post
Share on other sites
Sorry, I forgot to add a destructor in my interface code.

(I was at the office and a bit in a hurry to leave...)


  
class Bitmappable {
virtual void LoadBitmapFromFile (const char* szFilename) = 0 ;
virtual ~Bitmappable() {}
} ;
class Regionable {
virtual void LoadRegionFromFile (const char* szFilename) = 0 ;
virtual ~Regionable() {}
} ;



----
David Sporn AKA Sporniket

Share this post


Link to post
Share on other sites
Thanks to everyone for their replies, I finally got it working.

davidsporn: That''s the direction I was starting to lean in, and after I read your post that''s what I did, except I didn''t use pure virtual methods. Worked a charm.
But WinMaker *does* in fact have data-members, but that didn''t cause any ambiguities in SkinnedWinMaker, only the methods.

So again: Thanks everyone. Not only did I get it working, but I learnt something new in the process. Not a bad day.

-Neophyte

-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GED d- s:+ a- C++$ UL++ P++ L++ E W+ N+ o K? w !O M--(+) V-- PS+
PE Y+ PGP t-- 5++ X+ R(+) rv+(++) b+++ DI+ D(+) G e+>++ h r--> y+
----- END GEEK CODE BLOCK-----
geekcode.com

Share this post


Link to post
Share on other sites

  • Advertisement