The coding standards are the least of your worries!
Posted Jun 1, 2010 11:50 UTC (Tue) by rev
Parent article: GCC begins move to C++
For programmers going from C to C++ the most important issue is dealing with OO.
It is very easy to abuse C++'s OO features. The most common mistake is using inheritance where it not appropriate. Particularly when the relationship between two concepts is 1:1, programmers coming from C tend to use inheritance where composition is in order.
For instance, a HTTP connection has a TCP connection. For C programmers it is tempting to make a class representing a HTTP connection inherit from a class representing a TCP connection.
How can you see that this is wrong?
Well, suppose one day you need a HTTP connection over some other transport mechanism. Unfortunately your HTTP connection is intimately tied to a TCP connection (being a subclass it is a special kind of TCP connection after all). You cannot have a HTTP connection over some other transport mechanism without rewriting your HTTP connection class. Suppose you want to make your classes suitable for IPv6. You not only need to modify your TCP connection class but also your HTTP connection class.
Rather, you should supply the HTTP connection class (through the ctor) with an object representing a transport layer connection. (I.e. use composition). It has methods to read and write data, but whether it is a connection over TCP over IPv4, IPv6, or over some other transport layer, you don't care about. (Well, you also are likely to need to find a common interface for setting connection parameters dynamically, BTW. Finding commonalities amongst simmilar concepts is a common meme in OO.)
I did not make the above example up. It comes actually from a C++ library from the GNU project. The name escapes me. Sorry to say it was terribly designed. To get at the headers of a HTTP request, one had to subclass a HTTP connection class. There was no class representing a HTTP request to which one could simply say `getHeaders()'. It had knowledge about IPv6 in several layers of the class hierarchy.
Another common mistake is inherit to data rather than behavior. Having a class 'ellipse' inherit from a class 'circle' is a classic example. Rather `circle' and `ellipse' should both inherit from a class `shape' having a pure virtual method `draw()' that is implemented differently in its subclasses.
Needless to say the above abuse gets worse with every layer of subclassing one uses.
Some guiding principles:
- Use inheritance only when you are dealing with similar but different behaviors. I.e. only when one inherits to implement pure virtual functions.
- Related: favor composition over inheritance.
- Don't use setters, unless one deals with a naturally dynamically alterable property of an object. The hostname of a TCP connection should not be set using a setter (once connected, it is immutable). It should be ctor supplied.
- And related, all properties that are required to bring an object into a meaningful state should be supplied through the ctor.
- Isolate responsibilities into a class.
- Write down every piece of behavior only once.
- Variables are private and never public. They may be protected when subclasses need access to them.
- Avoid using `friend'. Don't use private and protected inheritance, these are meaningless.
- Don't use multiple inheritance. It is a can of worms. When you think you need it, you likely have violated the first two principles.
Sorry for the sermon. It's just that it gets to my nerves that when programmers talk about writing maintainable code they jump to the relatively minor issues showing to have a blind spot for the issues that really matter.
to post comments)