A few weeks ago I introudced private inheritance, and finished with a comment about a common excuse for using it. In this post I give an example of such usage, and discuss whether it is a good idea or not.
Say you are going to write a new class CppInfo
, which will print out some information about the C++ language. Here is an example:
class CppInfo { public: string info() { return "Language: C++\nCreator : Bjarne Stroustrup\nQuality : ??"; } };
Now we need some way to assess the quality of C++, to replace those question marks. Luckily, there exists a class that assesses the quality of a programming language:
class LanguageQuality { public: virtual ~LanguageQuality() {} double quality() const { return 100.0 / charsForHelloWorld(); } protected: virtual int charsForHelloWorld() const = 0; };
As you can see, the only thing we need to do is to create a derived class from LanguageQuality
for C++, returning the number of characters required for a C++ Hello, World
program.
This is where private inheritance can come in handy. We need access to LanguageQuality::quality()
, and we need to override charsForHelloWorld()
. If we chose to inherit privately from it in CppQuality
, we get both of these, yet none of the members of LanguageQuality
will be accessible by the users of that class (as shown in the last post).
Here is a version of CppInfo
that inherits privately from LanguageQuality
:
class CppInfo_UsingInheritance : private LanguageQuality { public: string info() { return "Language: C++\nCreator : Bjarne Stroustrup\nQuality : " + to_string(quality()); } private: int charsForHelloWorld() const { return string("#include <iostream>\nint main() { std::cout << "Hello World"; }").size(); } };
I have to admit, this looks pretty neat. The alternative is to create a new class whose only responsibilty is overriding charsForHelloWorld()
:
class CppQuality : public LanguageQuality { protected: int charsForHelloWorld() const { return string("#include <iostream>\nint main() { std::cout << \"Hello World\"; }").size(); } };
And then have this as a member in CppInfo
:
class CppInfo_UsingComposition { public: string info() { return "Language: C++\nCreator : Bjarne Stroustrup\nQuality : " + to_string(cppQuality.quality()); } private: CppQuality cppQuality; };
Now you have two classes instead of one, and 18 lines of code instead of 13. Which solution is best? I am not sure, so let’s have a look at the arguments:
The solution using inheritance is smaller. All the code is in one place, so it is easy to get an overview. We have however coupled our class CppInfo
very tightly to LanguageQuality
, even though their responsibilities aren’t directly related. Considering the single responsibility principle, CppInfo
is about printing general info about C++
, whereas LanguageQuality
is about computing the quality of a programming language.
I think the principle that provides the tipping point for me in this example, is that every piece of your software should have a single reason to change. Say we want to change how we compute the quality of a language (the number of characters required for Hello, World
just doesn’t cut it anymore). This would require a change to LanguageQuality
. We would then probably need to update all its derived classes. And to change a class, you really should understand all its responsibilities, invariants and effects. All the other code in CppInfo
has nothing to do with the change we are making, so we shouldn’t have to worry about it. Also, if we want to change what information we print out about C++ (say we want to add the publication year of the latest language standard), we want to understand how info is printed, not how the quality is computed.
Again, in a small example like this, it might not matter much, but in a larger example it will. And what starts out small has a nasty tendency to grow, so you’d better care about design from the very start.
Just for fun, here is the result after adding JavaInfo
and PythonInfo
:
Language: C++ Creator : Bjarne Stroustrup Quality : 1.587302 Language: Java Creator : James Gosling Quality : 0.970874 Language: Python Creator : Guido van Rossum Quality : 5.263158
Unsurprisingly, we beat Java, but lose to Python.
Now I am interested to hear your thoughts, which solution would you choose, and why?
As usual, the code for this blog post is available on GitHub.
If you enjoyed this post, you can subscribe to my blog, or follow me on Twitter.
Small typo: you have a curly bracket missing after the const on line 4 of CppQuality.
Thanks, fixed.
Maybe you could use CLang (if it can do it) to “grep” some existing source base (Chromium or FireFox for example) to find all private inheritances and inspect them.
That could be interesting. I don’t think you would need clang though, something like
$ find . -name '*.cpp' -o -name '*.h' | xargs grep 'class.*:.*private'
should do.I don’t however think I will take the time to actually do this, but if someone does, please let me know! :)
Good postt