Often when we create a class from scratch, it's only complete enough for the task at hand. But what if you want to create a library that's generally useful to the outside world? A class needs to be more complete and more robust. Hal Fulton discusses some decisions you need to make and things you should keep in mind for both internals and externals.

Like this article? We recommend

Like this article? We recommend

In any project, the highest priority is to get the job done. Rather than
being pretty, rather than being complete, the code must first work.

So when we (as programmers) create classes for use in custom applications, we
tend to cut corners. This is not a good thing, but we have all done it. We leave
out features that perhaps should belong to that class, or we skimp on activities
such as testing and documentation (especially the latter). This is not
necessarily carelessness; it's often the pressure of deadlines.

This is in contrast to our internal urge, which is to make a class complete
and robust. A surprising number of us even enjoy documentation if we are proud
of the work we have done on the code. The hacker's instinctand I use
the word hacker in the classic senseis to create well-rounded,
general-purpose tools. In this article, I will indulge that instinct.

After all, the class isn't always tangential to our purposes. What if we
want to distribute the class or library itself? In that case, this piece of code
is our main purpose.

What I want to discuss here is a set of guidelinesan incomplete set, of
course. It is more nearly complete in terms of interface design; much less so in
terms of implementation. If you want more complete advice, you should be looking
in books, not just articles. Read materials by Bjarne Stroustrup himself
if you want to go to the source or if you're heavily into theory; and read
Scott Meyers if you're a little more of a pragmatist. Both writers are
excellent.

What's more, I have no intention of teaching C++ or object-oriented
programming here. If you have no idea what a virtual function is, or how
inheritance works, refer to a tutorial.

Some of what I say will be common sense; other parts will be controversial.
In no sense is it a set of rigid inflexible rules or a checklist we should
follow mindlessly. My hope is that this material will help to jog your memory
("Oh yes, I forgot to do such-and-such"), will help correct little
defects and add useful features, and will ultimately result in a better piece of
code. If you remember the song a few years ago that said, "wear
sunscreen," you already know the kind of advice I am offering and the
spirit in which you should take it.

Getting Started

The question then is this: What makes a good class in C++? I can't
answer this simply or directly. Let me break it down into a set of questions and
decisions that a programmer should address.

First of all, is there any way you can leverage existing knowledge from the
industry and the community? If there is a relevant, well-known design
pattern, code consciously according to that pattern. A common example is the
Model-View-Controller pattern (MVC), in which the internal data
representation is kept separate from the external view of the data. This crops
up in everything from text editors to web-based shopping carts. A good
understanding of these frequently recurring patterns can act as a kind of mental
amplifier. I recommend the modern classic Design Patterns by the
"Gang of Four" (Erich Gamma et al, Addison-Wesley, 1995). This book
can serve you well, especially coding in a static language such as C++ or
Java.

Can you make your implementation easier by using the Standard Template
Library (STL)? Then don't be afraid to do so (unless some of your users
have older compilers without STL). If you reinvent the wheel, do it only for a
good reason. Do you need to make internal use of a stack or queue? Then use an
existing container class from the STL instead of creating your own.

The next question I would ask myself is: Why is this a class in the first
place? Bjarne Stroustrup identifies a kind of class which he calls a concrete
data typea data type that is created for convenience, but doesn't
necessarily fit into an inheritance hierarchy. In OOP terms, we would say that
its chief reason for being is encapsulation. An example is a date class that I
wrote several years ago (for handling times and dates in a high-level,
object-oriented fashion). Such a class doesn't inherit from anything else;
and we normally don't expect it to have child classes, either. Always ask
yourself whether this simple case holds true. In short, don't complicate a
design unnecessarily.