CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 11 of 11
  1. #1
    Join Date
    Jan 2004
    Location
    Netherlands
    Posts
    24

    Question When to define a common base class

    This is more a general OO question, but I was wondering when to define a base class and when not to. For instance, I have a couple of classes (not derived), all with a few common attributes but no common operations and also a lot of specific attributes. Also, I do not think these classes are subject to very much change. I can imagine that in a case where two classes only have one common attribute, that it would make no sense defining a base class.

    I read somewhere here on codeguru that it is not wise to automatically derive your classes in MFC from CObject because of the overhead. So I was wondering wether deriving a class in C++ has negative impact on the speed of your application and that it is faster to have seperate, unrelated classes. Or is this neglectable. I realize that there is probably not a clear guideline, but I was just curious about your opinions.

  2. #2
    Join Date
    Apr 1999
    Location
    Altrincham, England
    Posts
    4,470
    Is the common stuff similar because the two classes are fundamentally related to each other? In other words are the two classes specific examples of a single, more general, class?

    For example, "Car" and "Bus" would both have an engine because they are both specific examples of the more general "MotorVehicle" class. However, a "Radio" and a "Thermostat" might both have dials and displays, but it's hard to justify a relationship.

    Of course, the detail of what you are modelling plays a part here -
    "Sunflower" and "Zebra" may or may not share a base class depending on whether your model is of "LivingThings", or some lower classification (where animals are not related to plants).

    As a general rule, I would say "if in doubt, don't inherit". Certainly don't inherit just for the sake of reusing apparently similar members - there has to be a genuine exact correspondance between the members of the two classes, and the two classes should have a real hierarchical relationship with each other.
    Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure.
    --
    Sutter and Alexandrescu, C++ Coding Standards

    Programs must be written for people to read, and only incidentally for machines to execute.

    --
    Harold Abelson and Gerald Jay Sussman

    The cheapest, fastest and most reliable components of a computer system are those that aren't there.
    -- Gordon Bell


  3. #3
    Join Date
    Jan 2004
    Location
    Netherlands
    Posts
    24
    Well, I think my classes have enough in common to justify a common base class. But I am doubting if I should doubt I probably just need more experience in these matters to feel more confident in such decisions.

    But what about a possible negative impact on performance with inheritance in C++? Or does't it matter?

  4. #4
    Join Date
    Apr 1999
    Location
    Altrincham, England
    Posts
    4,470
    Can you post some details of the classes? Especially the bits that you think are common.
    Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure.
    --
    Sutter and Alexandrescu, C++ Coding Standards

    Programs must be written for people to read, and only incidentally for machines to execute.

    --
    Harold Abelson and Gerald Jay Sussman

    The cheapest, fastest and most reliable components of a computer system are those that aren't there.
    -- Gordon Bell


  5. #5
    Join Date
    Jan 2004
    Location
    Netherlands
    Posts
    24
    Code:
    class CLevel  
    {
    public:
    	bool visible;
    	int nr;
    	CString text;
    	HTREEITEM treePos;
    
    	CLevel(int a_level);
    	virtual ~CLevel();
    };
    
    class CSubgroup  
    {
    public:
    	bool visible;
    	int nr;
    	CString text;
    	HTREEITEM treePos;
    	std::map<int,CLevel> levels;
    
    	CSubgroup(int a_subgroup, int a_level);
    	virtual ~CSubgroup();
    };
    
    class CGroup  
    {
    public:
    	bool visible;
    	int nr;
    	CString text;
    	HTREEITEM treePos;
    	std::map<int,CSubgroup> subgroups;
    
    	CGroup(int a_group, int a_subgroup, int a_level);
    	virtual ~CGroup();
    };
    As you see they all have 4 attributes in common but they need very different constructors and I'm planning on some extra functions on CGroup. I already have this so it's extra effort making a base class but is is probably better.

    But I often come in a situation where I have to choose a hierarchy, so those general remarks are also very welcome.

  6. #6
    Join Date
    Apr 1999
    Location
    Altrincham, England
    Posts
    4,470
    Is there (or will there be) some functionality in your code that can use the common information only, without regard to any specific information? Do (or will you) have some code that can be supplied with an object of any of these three types and still work correctly (without resorting to downcasting)?
    Code:
    class Common
    {
    public:    // NB public data members are usually a BAD idea...
        bool visible;
        int nr;
        CString text;
        HTREEITEM treePos;
    
        virtual ~Common();
    };
    
    class CLevel  : public Common
    {
    public:
        CLevel(int a_level);
        virtual ~CLevel();
    };
    
    class CGroup : public Common
    {
    };
    // etc
    
    void func(Common & obj);
    
    int main()
    {
        CLevel lev;
        CGroup gro;
    
        func(lev);
        func(gro);
    }
    If the answer here is "yes", then public inheritance is probably warranted. If not, you might still consider packaging the common information into a class of its own and incorporating that as a member variable in each class.
    Code:
    class Common
    {
    public:
        bool visible;
        int nr;
        CString text;
        HTREEITEM treePos;
    };
    
    class CLevel  
    {
    public:
        Common basic_data;
    
        CLevel(int a_level);
        virtual ~CLevel();
    };
    
    // etc
    That way, you can still have common construction of the data, but without breaking the fundamental rule: public inheritance means IS-A.

    By the way, until you've decided to use inheritance, don't make your destructors virtual - you just end up with a vptr/vtable for no reason.
    Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure.
    --
    Sutter and Alexandrescu, C++ Coding Standards

    Programs must be written for people to read, and only incidentally for machines to execute.

    --
    Harold Abelson and Gerald Jay Sussman

    The cheapest, fastest and most reliable components of a computer system are those that aren't there.
    -- Gordon Bell


  7. #7
    Join Date
    Nov 2003
    Location
    Vienna, Austria
    Posts
    212
    There's no performance impact for plain deriving, in fact there's a code size advantage. There is a speed and size penalty for virtual functions. The size is the vtable and vptrs, the speed is the virtual call and loss of inlining (the latter is often the greater penalty).

    But unless you write something performance-critical, neither the gain nor the loss warrant a bad design.
    All the buzzt
    CornedBee

  8. #8
    Join Date
    May 2000
    Location
    KY, USA
    Posts
    18,652
    Originally posted by CornedBee
    ...and loss of inlining (the latter is often the greater penalty).
    Which of course depends on the compiler. There is no rule that virtual functions cannot be inlined. And many times the compiler could safely expand even virtual inline functions...
    Code:
    class CFoo
    {
    public:
      virtual void SetInteger(int Value) { Integer = Value; }
      virtual int GetInteger() { return Integer; }
    
    private:
      int Integer;
    }
    
    
    class CFoo2 : public CFoo
    {
    public:
      virtual void SetInteger(int Value) { OtherInteger = Value; }
      virtual int GetInteger() { return OtherInteger; }
    
    private:
      int OtherInteger;
    }
    
    int main()
    {
      CFoo Object;
    
      int i = Object.GetInteger();
    
      return 0;
    }
    In this example, the compiler clearly knows that 'Object' is of type 'CFoo', there is no way, that it could be a 'CFoo2' since it is explicitly declared. So, the compiler could expand the inline function without any problems.

    Nevertheless, most of the compilers nowadays will go the safe route...and that is the guarantee, that polymorphism has to work...no matter what...

  9. #9
    Join Date
    Feb 2003
    Posts
    377
    This question reminds me of a guideline from Herb Sutter's Exceptional C++:
    Never inherit publicly to reuse code (in the base class); inherit publicly in order to be reused (by code that uses base objects polymorphically).*

    *I got this guideline from (and use it with thanks to) Marshall Cline, who is co-author of the classic C++ FAQs book (Cline 95).
    That basically says what Graham was saying. More information here: http://www.gotw.ca/gotw/014.htm.

  10. #10
    Join Date
    Jan 2004
    Location
    Netherlands
    Posts
    24
    Originally posted by Graham
    Is there (or will there be) some functionality in your code that can use the common information only, without regard to any specific information? Do (or will you) have some code that can be supplied with an object of any of these three types and still work correctly (without resorting to downcasting)?

    If the answer here is "yes", then public inheritance is probably warranted. If not, you might still consider packaging the common information into a class of its own and incorporating that as a member variable in each class.
    Well, I think the answer is no. Basically, they only share a couple of attributes, so packaging these attributes should be enough. By the way, why package these attributes in a class and not in a struct?
    By the way, until you've decided to use inheritance, don't make your destructors virtual - you just end up with a vptr/vtable for no reason.
    That was not an explicit choise of mine, somehow Visual C++ did that for me. These things are great to know for optimizing my code
    Originally posted by CornedBee
    There's no performance impact for plain deriving, in fact there's a code size advantage. There is a speed and size penalty for virtual functions. The size is the vtable and vptrs, the speed is the virtual call and loss of inlining (the latter is often the greater penalty).

    But unless you write something performance-critical, neither the gain nor the loss warrant a bad design.
    OK, that is good to know. I don't write a performance-critical or realtime application but I also do not want unnecessary performance drops.
    Originally posted by jlou
    More information here: http://www.gotw.ca/gotw/014.htm.
    Very interresting link. But I'm still a bit confused about public inheritance. I only know one way of inheritance and that will probably be public inheritance.

  11. #11
    Join Date
    Nov 2003
    Location
    Vienna, Austria
    Posts
    212
    Which of course depends on the compiler. There is no rule that virtual functions cannot be inlined. And many times the compiler could safely expand even virtual inline functions...
    Of course. I wonder which do.
    All the buzzt
    CornedBee

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured