Some Thoughts on Writing Classes

Some Thoughts on Writing Classes

By Francis Glassborow

Overload, 1(1):, April 1993


As some of you know I now present some of Richford's courses for serious programmers converting to C++. One of the benefits of such involvement is being made aware of the problems that face experienced pro­grammers when they first start writing their own class­es. The following are a few thoughts based on my observations.

Never Make Data public

I know it is possible to make data public but doing so reveals a lack of understanding of the nature of 'data hiding' and object-orientation. Even making a single data member public breaches your data security and allows others to get at your data. More importantly it has ensured that any time you use such a class you will need to check all your source code when you decide to change your data structure. Do I hear you murmur that you will never want to make such a change? You may be right, but why take the risk? One of the key advan­tages to OOP is that you get a payback for the resourc­es invested in writing re-usable code when you come to maintain the packages using it.

Keep to Simple Data Structures

A class should do one clearly defined thing - usually provide a new user-defined type. If you feel that the best way of implementing some aspect of your class is via some other data-structure (eg a linked list) imple­ment such a class separately.

I recently found myself trying to implement a linked list class without first defining a node class. Madness, utter madness! When I realised my insanity it was easy to go back, write my node class and then use it for my linked list class.

Understand Inheritance

When programmers first meet inheritance it is often like a new shiny toy and they want to use it all the time. Inheritance is an excellent tool when applied properly but you should not get in the habit of warping your view of the world in order to use it. Such a mindset leads not only to over-use of simple inheritance but creates a view that leads to quite unnecessary use of multiple in­heritance.

To my mind, inheritance is the basic tool for code re­use. It is an extension of the idea of a library of func­tions. Good programmers have always been alert to the possibility of writing simple functions as building blocks for more complicated ones. They store the simple ones in personal libraries so that they can re­use them. Inheritance provides the method fora more sophisticated approach and one where you never have to touch clean, fully-debugged code. Indeed, if you do go back to tweak your code simply for the bene­fit of some program there is a fundamental blind-spot in your perception of OOP. Any alteration to the public interface of a fully developed class is a serious breach of the unwritten agreement between your class and the code that uses it. If you want to make changes do it via inheritance; that is what it is for.

Understand Polymorphism

There seem to be two quite distinct ideas about this. On the one hand there are those who think that poly­morphism is simply a version of overloading. I think this is an abuse of the word. To me, and many others, polymorphism relates to runtime selection of a func­tion or of a function's behaviour. It is possible, via mechanisms such as switch statements, to provide polymorphism in languages such as C. C++ provides a special mechanism, virtual member functions, to support polymorphism. It needs the support of inherit­ance but is a separate and distinct C++ resource.

Unless your class structure needs polymorphism (i.e. must delay some decisions till runtime) do not clutter your classes with virtual functions. Not only is there some slight runtime cost in both code size and speed but you are setting a trap for yourself. You should be writing your code so that errors can be detected as early as possible, preferably by the compiler. I have even seen a suggestion that we should design pro­grammer's editors that will detect syntax errors (at the back of my mind lurks the thought that I saw some such editor about eighteen months ago).

Appreciate C++ Mechanisms

Quite a number of mechanisms have been introduced in C++ to sidestep some of the more awkward meth­ods of C. You may argue for some time about the rela­tive advantages of using #define and const float to provide the value of pi in your work and I can't see any great advantage one way or other.

When it comes to providing macros I think that C++ template functions win hands down. They aren't total­ly problem free but they are very substantial improve­ment on C's use of #define. I think that programmers moving from C should always ask themselves if their use of #define is appropriate.

Both C and C++ make extensive use of pointers but most C pointers should be converted to references in C++. The strength of the C++ pointer is to be more economical with your use of stack space (always as­suming that your compiler implements parameter passing and local variables via a stack). C program­mers are parsimonious with using malloc and free, C++ programmers should be profligate with the C++ equivalents of new and delete. The mechanism for getting dynamic storage has been made easy to en­courage you to use it.

Know What the Defaults Will Be

There are a number of places where C++ provides de­faults in addition to those that already plague C pro­grammers (by the way, do you know if your code relies on char being specifically signed or unsigned?).

If your class does not need a meaning for assignment inhibit the default. If your class contains a pointer write your own copy constructor. If you introduce a pointer into a class re-write its copy constructor.

Remember that inheritance is private by default (un­less you are one of those oddballs who insist on using struct to declare active data-function combinations).

Remember that the onus for cleaning up is yours, not the operating system's. Just because your OS cleans up for you does not mean that you should rely on it. In fact I could argue strongly that it is not the job of an OS to tidy up and those that do so are pre-empting my right to pass data blocks around. Why shouldn't I get some dynamic memory and pass its address to anoth­er program? If the OS doesn't realise that I have passed access to another task and recovers the space when the initiating task ends then my programs will be in a mess.

Don't Use Cast to Silence the Compiler

Cast is powerful tool. I always think of it as a message to the compiler that goes something like this:

I know what I am doing so do it my way.

As long as the compiler can make sense of your syn­tax it will do it regardless of the consequences. You should never use a cast if you do not know exactly what it will cause to happen. Just because it silences the compiler does not mean that what you have written is correct, all it means is that the compiler can produce some code that it thinks will meet your instructions.

Conclusion

I wonder how many of the above points are ones that you dis-agree with? Or how many other points would you pass on to those converting to OOP in C++? Please send your points to Mike Toms.






Your Privacy

By clicking "Accept Non-Essential Cookies" you agree ACCU can store non-essential cookies on your device and disclose information in accordance with our Privacy Policy and Cookie Policy.

Current Setting: Non-Essential Cookies REJECTED


By clicking "Include Third Party Content" you agree ACCU can forward your IP address to third-party sites (such as YouTube) to enhance the information presented on this site, and that third-party sites may store cookies on your device.

Current Setting: Third Party Content EXCLUDED



Settings can be changed at any time from the Cookie Policy page.