DiscussionUsing inheritance when it is not needed betrays a misplaced faith in the power of object orientation. In C++, you need to do specific things when defining base classes (see also Items 32, 50, and 54), and very different and often contrary things when designing standalone classes. Inheriting from a standalone class opens your code to a host of problems, very few of which will be ever flagged by your compiler. Beginners sometimes derive from value classes, such as a string class (the standard one or another), to "add more functionality." However, defining free (nonmember) functions is vastly superior to creating super_string for the following reasons:
So, prefer to add functionality via new nonmember functions (see Item 44). To avoid name lookup problems, make sure you put them in the same namespace as the type they are meant to extend (see Item 57). Some people dislike nonmember functions because the invocation syntax is Fun(str) instead of str.Fun(), but this is just a matter of syntactic habit and familiarity. (Then there's the sound bite, attributed to the legendary Alan Perlis, that too much syntactic sugar causes cancer of the semicolon.) What if super_string wants to inherit from string to add more state, such as encoding or a cached word count? Public inheritance is still not recommended, because string is not protected against slicing (see Item 54) and so any copying of a super_string to a string will silently chop off all of the carefully maintained extra state. Finally, inheriting from a class with a public nonvirtual destructor risks littering your code with undefined behavior by delete-ing pointers to string that actually point to super_string objects (see Item 50). This undefined behavior might seem to be tolerated by your compiler and memory allocator, but it leaves you in the dark swamp of silent errors, memory leaks, heap corruptions, and porting nightmares. |