Overloading or obfuscation?
Operator overloading is a powerful feature of the C++ language. Used with care, it can yield readable, maintainable and efficient code. However, it is just as easy to write "clever" code which is apparently elegant, but, in reality, is hard to understand and, hence, unmaintainable. This article gives examples of good and bad practice and investigates how and where an embedded developer might make good use of operator overloading.
What is the key objective when you write some code? The obvious answer is that you want to impart some specific functionality to the device that you are programming. That would be fine, if just designing and writing code was all that a software engineer was required to do. There are several programming languages that would enable code to be written very rapidly. An example, that I have an affection for, is Forth. With this language you can crank code very quickly indeed and it can be quite efficient, particularly on memory footprint. The problem comes when you try to read the code later. I have heard people describe Forth as a “write only programming language” and I can see their point.
This is the crux of the issue. Developers only spend a small proportion of their time writing new code. They expend a good deal more effort maintaining and enhancing existing software. So, the real answer to my initial question should be that you are communicating the design of your algorithms and procedures to the poor guy who will maintain the code at some future point (which may, of course, be you!).
So, writing clear, well commented code is vital. This rather puts the nail in the coffin for Forth, but opens up some possibilities in languages like C and, more so, C++. The key to writing clear, easy to understand code is to aim for its functionality to be obvious by making everything as intuitive as possible. Identifiers should have meaningful names – that is a good start. C++ classes should behave in obvious ways. Things like enums should be exploited widely. Also, operators should be overloaded to make the code more readable.
Operator overloading for clarity
Operator overloading is very neat. When you define a class, you specify some data (member variables) and operations that might be performed on/using that data (member functions). You can also include some special member functions that implement the standard C++ language operators (like +, -, *, >>, etc.) for your class. For example, imagine you have two objects, instantiated from the same class, called alpha and beta and you wish to add beta to alpha (we do not need to know exactly what "add" means in this context). This might be done using a member function like this:
But this is not very intuitive. An alternative is to overload the += operator so that you can write:
alpha += beta;
This is much clearer. You could also overload + (and =) so that the operation could be coded this way:
alpha = alpha + beta;
This is ideal for all frustrated Basic, Pascal and FORTRAN programmers.
The syntax for operator overloading is reasonably straightforward. The code to implement the operator functionality is simply a class member function with a special name. This name is constructed from the keyword operator and the operator symbol or name that is to be overloaded. Here is a fairly self-explanatory example:
void stuff::operator+=(unsigned val)
data += val;
Currently no items