The Well-Crafted Class in C++: Part 2

The well-crafted C++ library should be full-featured and complete. In this last article of his series, Hal Fulton emphasizes that the developer should also pay special attention to areas such as testing and documentation, both of which are neglected too often.

Like this article? We recommend

In the first
article of this series, I talked about some issues surrounding the careful
creation of a quality class or library in C++. You looked at constructors, methods
and parameter lists, and issues with inheritance. Most of these issues are just
good OOP practices mixed with common sense.

You may have noticed that I didn't always separate interface issues from
implementation issues. I'm not stressing that separation here, either.

That distinction exists for the library user, to be sure. When I call a
method that happens to access an internal data structure, I should not generally
care whether that structure is an array or a B-tree. To keep a stable
application programming interface (API), we make this separation between
interface and implementation. Besides assuring future compatibility, it also
prevents our having to change documentation in most cases.

But the programmer who is designing the library doesn't have the luxury
of dealing only with the interface. Obviously, the interface has to have an
implementation behind it. With that in mind, let's proceed.

Effective Operator Overloading

C++ makes operator overloading possible. Where appropriate, go ahead and use
this feature. But when you do so, make sure that you choose the operators in a
sensible way, and make sure that there are no "holes" in the set of
operators. If it makes sense to define a +, it probably makes sense to
define a  also (although sometimes it may not). If you define
+ and -, you probably want to define += and -= also.

Logically, you should avoid repeating code. Define the more complex functions
in terms of the simpler ones; for example, let += call the +
operator. On the other hand, if you want to avoid the creation of a new object
when += is invoked, violate this rule and duplicate the code.

Of course, in such a simple case, code duplication is not a real issue, and
here we haven't actually duplicated anything word for word, anyhow. In
other cases, the common code might be several lines, in which case you might
wish to put it in a common private method, to be called more than once.

If you define ++ and -- operators, make sure that you define
both forms (pre- and post-). Make sure also that they behave in the traditional
way we expect. The prefix form does the increment before the value is
returned; the postfix form saves the value to be returned and then does
the increment. There's nothing in C++ to enforce thisyou have to
follow the convention consciously.

There might be some operators (such as () and ->) that you
rarely want to define. If you simply don't want them to be used, you can
always just ignore them. Alternatively, you could raise an exception that
explicitly points out that these are not implemented.

If inheritance is an issue, there is a third and better alternative. You
could make these virtual functions so that child classes could define them as
needed.