Thread: to inherit or not to inherit ...

  1. #1
    Registered User
    Join Date
    Jul 2008
    Posts
    67

    to inherit or not to inherit ...

    Moin,

    I need an advice in the following case ...

    I want to create a control from scratch, a Tab Control.

    In the moment I've created a simple struct for the TabItems and a class for the TabWindow.

    Code:
    struct TabItem {
        LPCTSTR  itemText;
        unsigned itemID;
        RECT       itemRect;
    };
    
    class TabWindow {
        /* Con- / Destructors
           ...  */
      private:
        TabItem* tabItems; // pointer to an array of TabItems
        /* ... */
    };
    Does it make sense, or do I get advantages, if I let TabWindow derive from TabItem ?
    Or is it unnessecery, because there are no advantages ...

    Code:
    class TabItem {
        LPCTSTR  itemText;
        unsigned itemID;
        RECT       itemRect;
    };
    
    class TabWindow : private TabItem {
        /* Con- / Destructors
           ...  */
      private:
        TabItem* tabItems; // pointer to an array of TabItems
        /* ... */
    };
    Thanks for reply.


    Greetz
    Greenhorn

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Why would you derive? A TabWindow is not a TabItem, which derivation would seem to imply. A TabWindow does have TabItems in it, so containment seems more likely here.

  3. #3
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Is a TabWindow a TabItem?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  4. #4
    Registered User
    Join Date
    Jul 2008
    Posts
    67
    Quote Originally Posted by laserlight View Post
    Is a TabWindow a TabItem?
    A Tab Window/Control is a container with Tab Items (Rectangles).

    @tabstop
    That's what I wanted to read, thank you very much.

    I just thought about it, because a Tab Window without Tab Items is nothing and reversed it's also the case.
    At first I thought I get a gain from inheritance, but in fact it is more comlicated and more work without a good reason, I think.

    Thanks to both of you for taking notice and giving advice.


    Greetz
    Greenhorn

  5. #5
    Registered User
    Join Date
    Jul 2003
    Posts
    110
    Since you are using private inheritance, the relationship isn't expressed by "IS A." Private inheritance is more "WORKS LIKE A."

    As I understand it, unless there are virtual functions that you need to override, you should prefer composition to private inheritance. Less coupling.

  6. #6
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    To make the discussion more general, you would want class B to inherit from class A only if B is intended to present the same interface (possibly with extensions) of A. As LaserLight said, one way of looking at this is a "is-a" relationship. But in the face of pure interfaces, B inheriting from A could also indicate that B always presents the interface defined by A, without necessarily "being" an A.

    If B is not naturally an A, or implements A, then don't force some peculiar definition on B just so that it conforms to A. Inheritance is useful but you should always ask youself: if B inherits from A, then if a user of B casts a B& to an A&, does everything still make sense? If not, then inheritence is not the right method.

    Code reuse can be accomplished in other ways than just inheritance.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  7. #7
    Registered User
    Join Date
    Jul 2003
    Posts
    110
    Quote Originally Posted by brewbuck View Post
    If B is not naturally an A, or implements A, then don't force some peculiar definition on B just so that it conforms to A. Inheritance is useful but you should always ask youself: if B inherits from A, then if a user of B casts a B& to an A&, does everything still make sense? If not, then inheritence is not the right method.
    Does this apply to private inheritance though? Unless the OP simply confused things (which is quite possible), then by inheriting privately there is no risk of a cast from B to A.

  8. #8
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by whoie View Post
    Does this apply to private inheritance though? Unless the OP simply confused things (which is quite possible), then by inheriting privately there is no risk of a cast from B to A.
    In the case of private inheritance I think it becomes a matter of syntax. Whether B "is-a" A or B "contains-a" A is not really relevant to outside users of B, because A is not publicly accessible in the first place. You can always simulate private inheritance with composition, so this is really just an internal design decision by the maker of class B.

    There are designs which use protected virtual functions to access private implementation details but I don't like those designs.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  9. #9
    Registered User
    Join Date
    Jul 2003
    Posts
    110
    Quote Originally Posted by brewbuck View Post
    In the case of private inheritance I think it becomes a matter of syntax. Whether B "is-a" A or B "contains-a" A is not really relevant to outside users of B, because A is not publicly accessible in the first place. You can always simulate private inheritance with composition, so this is really just an internal design decision by the maker of class B.

    There are designs which use protected virtual functions to access private implementation details but I don't like those designs.
    I don't think you can always avoid private inheritance with composition, but I understand that you want to when you can. If the base class has virtual functions that you have to override, then I think you are stuck with inheriting one way or another. Perhaps you could make a special derivation to override the virtual functions and then aggregate that instead. Hmm...

    Are you talking about the C++ IOStreams library as an example of designs that use protected virtual functions to access implementation details, or something else?

  10. #10
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by whoie View Post
    Are you talking about the C++ IOStreams library as an example of designs that use protected virtual functions to access implementation details, or something else?
    I was just speaking in general. If class A uses protected virtual functions to access parts of its implementation, then private inheritance can't be simulated with composition, because composed objects only expose a public interface. Whether to inherit or contain should be a decision left to the architect, not forced by class A.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  11. #11
    Registered User
    Join Date
    Jul 2008
    Posts
    67
    In my case Class A is only used/manipulated by class B.

    I don't need any functions in class A, because it's all done by class B.
    So I decide not to inherit from A.
    Laserlight said it: A TabWindow is not a TabItem, even class B can't live without A and A can't without B.

    Here's my current header file ...
    (Please, don't judge too hard, I'm still on a learning curve in C/C++)
    Code:
    struct TAB_ITEM {
    
    	unsigned	itemID;
    	LPCTSTR		itemText;
    	int			itemImage;
    	RECT		itemRect;
    };
    
    /*
     * Die Fensterklasse
     */
    class TabCtrl {
    public:
    	TabCtrl ();
    	TabCtrl (HWND hwnd);	// Parent-Fenster als Argument
    	~TabCtrl ();
    
    	HWND CreateTabWindow (HINSTANCE hInst, HWND hParent, int X, int Y, int Width, int Height);
    	bool InsertTabItem (HWND hwnd, unsigned numID);
    	bool DeleteTabItem (HWND hwnd, unsigned numID);
    	bool SetTabCurSelItem (HWND hwnd, unsigned index);
    	static LRESULT CALLBACK TabWndProc (HWND hwnd, unsigned msg, WPARAM wParam, LPARAM lParam);
    	LRESULT TabMsgProc (unsigned msg, WPARAM wParam, LPARAM lParam);
    
    private:
    	HWND		tabWnd;					// Handle - Tabfenster
    	HWND		tabWndParent;			// Handle - Parentfenster
    	LPCTSTR		tabClassName;			// Name der Fensterklasse
    	TAB_ITEM*	tabItems;				// Zeiger auf ein Array mit TabItems
    	unsigned	tabNumItems;			// Anzahl der TabItems
    	unsigned	tabItemFirstVisible;	// Erstes sichtbare TabItem
    	unsigned	tabItemLastVisible;		// Letztes sichtbare TabItem
    	unsigned	tabItemMinWidth;		// Minimale Breite der TabItems
    	unsigned	tabItemHPadding;		// "Polster" zwischen Text und Icon (horizontal)
    	unsigned	tabCurSelItem;			// Aktuell gewähltes TabItem
    	RECT		tabBarBtn;				// TabBar-Button
    
    	bool RegisterTabClass (HINSTANCE hInstance);
    	bool SetTabItem (unsigned index);
    	bool ResetTabItems ();
    	bool UpdateTabItems ();
    
    };

    Greetz

  12. #12
    Registered User
    Join Date
    Jul 2003
    Posts
    110
    Quote Originally Posted by brewbuck View Post
    Whether to inherit or contain should be a decision left to the architect, not forced by class A.
    I generally agree, except for cases like the iostreams one where they have to fit in a family of classes to work. There is a whole heck of a lot of code that I can leverage there by simply deriving from streambuf and overriding a couple of protected virtual functions, and maybe writing a few additional functions. Hard to argue with that design IMO.

  13. #13
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Stream buffers use public derivation, though, so the situation is different anyway.

    They merely use protected virtual functions to conform to the "non-virtual interface" design pattern.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  14. #14
    Registered User
    Join Date
    Jul 2003
    Posts
    110
    Quote Originally Posted by CornedBee View Post
    Stream buffers use public derivation, though, so the situation is different anyway.

    They merely use protected virtual functions to conform to the "non-virtual interface" design pattern.
    Agreed, but the conversation drifted to the generally speaking. The architect still is forced to derive instead of compose, and I don't necessarily agree that is inherently evil.

    I can't think of a time when I've used private inheritance, but I wouldn't shun it out of hand if it appeared to offer a good reuse opportunity. It is one case where code reuse can justify the derivation (as long as there are virtual functions that "call back" to the derived class). As for the example that started this thread, then I agree it would have been a poor choice.

  15. #15
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Scanning my largest and most complex C++ project, the only case where I've used private inheritance is to derive from boost::noncopyable, and that's a very special base class.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 8
    Last Post: 12-02-2008, 12:45 PM
  2. Class design (inherit or not)
    By Raigne in forum Game Programming
    Replies: 4
    Last Post: 03-03-2008, 02:10 PM
  3. Friend cannot inherit from private subclass
    By MWAAAHAAA in forum C++ Programming
    Replies: 4
    Last Post: 11-19-2006, 04:44 PM
  4. Replies: 4
    Last Post: 12-29-2002, 12:29 AM
  5. Replies: 1
    Last Post: 11-27-2001, 01:07 PM