The C++ Source is pleased
to present an excerpt from Herb and Andrei's new book, C++ Coding Standards: 101
Rules, Guidelines, and Best Practices (Addison-Wesley, 2005).
More than just style guidelines and "gotcha" warnings, this book clarifies
the idioms and practices that pertain specifically to successful C++
software. Even better, you can't avoid deepening your mastery of the
finer points of C++ as you read. This is the singularly authoritative
Writ of Common Wisdom for the entire C++ development experience.

Discussion

Your compiler is your friend. If it issues a warning for a certain construct, often
there's a potential problem in your code.

Successful builds should be silent (warning-free). If they aren't,
you'll quickly get into the habit of skimming the output, and you
will miss real problems. (See Item 2.)

To get rid of a warning: a) understand it; and then b) rephrase your
code to eliminate the warning and make it clearer to both humans and
compilers that the code does what you intended.

Do this even when the program seemed to run correctly in the first place. Do this
even when you are positive that the warning is benign. Even benign warnings can
obscure later warnings pointing to real dangers.

Examples

Example 1: A third-party header file. A library header file that
you cannot change could contain a construct that causes (probably benign)
warnings. Then wrap the file with your own version that
#includes the original header and selectively turns
off the noisy warnings for that scope only, and then
#include your wrapper throughout the rest of your
project. Example (note that the warning control syntax will vary from
compiler to compiler):

Example 2: "Unused function parameter." Check to make sure you really didn't mean
to use the function parameter (e.g., it might be a placeholder for future expansion, or
a required part of a standardized signature that your code has no use for). If it's not
needed, simply delete the name of a function parameter:

Example 3: "Variable defined but never used." Check to make
sure you really didn't mean to reference the variable. (An RAII
stack-based object often causes this warning spuriously; see Item 13.) If
it's not needed, often you can silence the compiler by inserting an
evaluation of the variable itself as an expression (this evaluation won't
impact run-time speed):

Example 4: "Variable may be used without being initialized."
Initialize the variable (see Item 19).

Example 5: "Missing return." Sometimes the
compiler asks for a return statement even though your
control flow can never reach the end of the function (e.g., infinite loop,
throw statements, other returns).
This can be a good thing, because sometimes you only think that control
can't run off the end. For example, switch statements
that do not have a default are not resilient to change
and should have a default case that does
assert( false ) (see also Items 68 and 90):

Example 6: "Signed/unsigned mismatch." It is usually not
necessary to compare or assign integers with different signedness. Change
the types of the variables being compared so that the types agree. In the
worst case, insert an explicit cast. (The compiler inserts that cast for
you anyway, and warns you about doing it, so you're better off putting it
out in the open.)

Exceptions

Sometimes, a compiler may emit a tedious or even spurious warning (i.e.,
one that is mere noise) but offer no way to turn it off, and it might be
infeasible or unproductive busywork to rephrase the code to silence the
warning. In these rare cases, as a team decision, avoid tediously working
around a warning that is merely tedious: Disable that specific warning
only, disable it as locally as possible, and write a clear comment
documenting why it was necessary.

References

12. Know when and how to code for concurrency.

Summary

Thsareafedly:
If your application uses multiple threads or processes, know how to
minimize sharing objects where possible (see Item 10) and share the right
ones safely.

Discussion

Threading is a huge domain. This Item exists because that domain is
important and needs to be explicitly acknowledged, but one Item can't do
it justice and we will only summarize a few essentials; see the References
for many more details and techniques. Among the most important issues are
to avoid deadlocks, livelocks, and malign race conditions (including
corruption due to insufficient locking).

The C++ Standard says not one word about threads. Nevertheless, C++ is routinely
and widely used to write solid multithreaded code. If your application shares data
across threads, do so safely:

Prefer to wrap the platform's primitives in your own
abstractions: This is a good idea especially if you need
cross-platform portability. Alternatively, you can use a library (e.g.,
pthreads [Butenhof97]) that does
it for you.

Ensure that the types you are using are safe to use in a
multithreaded program: In particular, each type must at minimum:

Guarantee that unshared objects are independent: Two threads
can freely use different objects without any special action on the
caller's part.

Document what the caller needs to do to use the same object of that
type in different threads: Many types will require you to serialize
access to such shared objects, but some types do not; the latter typically
either design away the locking requirement, or they do the locking
internally themselves, in which case, you still need to be aware of the
limits of what the internal locking granularity will do.

Note that the above applies regardless of whether the type is some
kind of string type, or an STL container like a
vector, or any other type. (We note that some authors
have given advice that implies the standard containers are somehow
special. They are not; a container is just another object.) In
particular, if you want to use standard library components (e.g.,
string, containers) in a multithreaded program,
consult your standard library implementation's documentation to see
whether that is supported, as described earlier.

When authoring your own type that is intended to be usable in a
multithreaded program, you must do the same two things: First, you must
guarantee that different threads can use different objects of that type
without locking (note: a type with modifiable static data typically can't
guarantee this). Second, you must document what users need to do in order
to safely use the same object in different threads; the fundamental design
issue is how to distribute the responsibility of correct execution
(race-and deadlock-free) between the class and its client. The main
options are:

External locking: Callers are responsible for locking. In this
option, code that uses an object is responsible for knowing whether the
object is shared across threads and, if so, for serializing all uses of
the object. For example, string types typically use external locking (or
immutability; see the third option on the next page).

Internal locking: Each object serializes all access to itself,
typically by locking every public member function, so that callers may not
need to serialize uses of the object. For example, producer/consumer
queues typically use internal locking, because their whole raison
d'être is to be shared across threads, and their interfaces are
designed so that the appropriate level of locking is for the duration of
individual member function calls (Push,
Pop). More generally, note that this option is
appropriate only when you know two things:

First, you must know up front that objects of the type will nearly always be
shared across threads, otherwise you'll end up doing needless locking. Note
that most types don't meet this condition; the vast majority of objects even in a
heavily multithreaded program are never shared across threads (and this is
good; see Item 10).

Second, you must know up front that per-member-function locking is at
the right granularity and will be sufficient for most callers. In
particular, the type's interface should be designed in favor of
coarse-grained, self-sufficient operations. If the caller typically needs
to lock several operations, rather than an op- eration, this is
inappropriate; individually locked functions can only be assembled into a
larger-scale locked unit of work by adding more (external) locking. For
example, consider a container type that returns an iterator that could
become invalid before you could use it, or provides a member algorithm
like find that can return a correct answer that could become the wrong
answer before you could use it, or has users who want to write
if( c.empty() ) c.push_back(x);. (See [Sutter02] for additional examples.)
In such cases, the caller needs to perform external locking anyway in
order to get a lock whose lifetime spans multiple individual member
function calls, and so internal locking of each member function is
needlessly wasteful.

So, internal locking is tied to the type's public interface: Internal
locking becomes appropriate when the type's individual operations are
complete in themselves; in other words, the type's level of abstraction is
raised and expressed and encapsulated more precisely (e.g., as a
producer-consumer queue rather than a plain vector).
Combining primitive operations together to form coarser common operations
is the approach needed to ensure meaningful but simple function calls.
Where combinations of primitives can be arbitrary and you cannot capture
the reasonable set of usage scenarios in one named operation, there are
two alternatives: a) use a callback-based model (i.e., have the caller
call a single member function, but pass in the task they want performed as
a command or function object; see Items 87 to 89); or b) expose locking in
the interface in some way.

Lock-free designs, including immutability (read-only objects): No
locking needed. It is possible to design types so that no locking at
all is needed (see References). One common example is immutable objects,
which do not need to be locked because they never change; for example, for
an immutable string type, a string object is never modified once created,
and every string operation results in the creation of a new string.

Note that calling code should not need to know about your types'
implementation details (see Item 11). If your type uses under-the-covers
data-sharing techniques (e.g., copy-on-write), you do not need to take
responsibility for all possible thread safety issues, but you must take
responsibility for restoring "just enough" thread safety to guarantee that
calling code will be correct if it performs its usual duty of care: The
type must be as safe to use as it would be if it didn't use covert
implementation-sharing. (See [Sutter04c].) As noted, all properly
written types must allow manipulation of distinct visible objects in
different threads without synchronization.

Particularly if you are authoring a widely-used library, consider
making your objects safe to use in a multithreaded program as described
above, but without added overhead in a single-threaded program. For
example, if you are writing a library containing a type that uses
copy-on-write, and must therefore do at least some internal locking,
prefer to arrange for the locking to disappear in single-threaded builds
of your library (#ifdefs and no-op implementations are
common strategies).

When acquiring multiple locks, avoid deadlock situations by arranging for all code
that acquires the same locks to acquire them in the same order. (Releasing the locks
can be done in any order.) One solution is to acquire locks in increasing order by
memory address; addresses provide a handy, unique, application-wide ordering.

References

34. Prefer composition to inheritance.

Summary

Avoid inheritance taxes: Inheritance is the second-tightest coupling relationship in
C++, second only to friendship. Tight coupling is undesirable and should be
avoided where possible. Therefore, prefer composition to inheritance unless you
know that the latter truly benefits your design.

Discussion

Inheritance is often overused, even by experienced developers. A sound rule of
software engineering is to minimize coupling: If a relationship can be expressed in
more than one way, use the weakest relationship that's practical.

Given that inheritance is nearly the strongest relationship we can express in C++,
second only to friendship, it's only really appropriate when there is no equivalent
weaker alternative. If you can express a class relationship using composition alone,
you should prefer that.

In this context, "composition" means simply embedding a member variable of a
type within another type. This way, you can hold and use the object in ways that al-
low you control over the strength of the coupling.

Composition has important advantages over inheritance:

Greater flexibility without affecting calling code: A private
data member is under your control. You can switch from holding it by
value to holding by (smart) pointer or Pimpl (see Item 43) without
breaking client code; you would only need to change the implementations of
the class's own member functions that use it. If you decide you need
different functionality, you can easily change the type of the member or
the manner of holding it while keeping the class's public interface
consistent. In contrast, if you begin with a public inheritance relation-
ship, it is likely that clients have already come to depend on the
inheritance; you have therefore committed your class to it and cannot
easily change your base class decision later on. (See Item 37.)

Greater compile-time insulation, shorter compile times: Holding
an object by pointer (preferably a smart pointer), rather than as a direct
member or base class, can also allow you to reduce header dependencies
because declaring a pointer to an object doesn't require that object's
full class definition. By contrast, inheritance always requires the full
definition of the base class to be visible. A common technique is to
aggregate all private members behind a single opaque pointer, called a
Pimpl (see Item 43).

Less weirdness: Inheriting from a type can cause name lookup to
pull in functions and function templates defined in the same namespace as
that type. This is very subtle and hard to debug. (See also Item 58.)

Wider applicability: Some classes were not designed to be bases
in the first place (and see Item 35). Most classes, however, can fulfill
the role of a member.

Great robustness and safety: The tighter coupling of
inheritance makes it more dif- ficult to write error-safe code. (See [Sutter02] §23.)

Less complexity and fragility: Inheritance exposes you to
additional complications, such as name hiding and other complications that
can arise in the presence of later changes to the base class.

Of course, these are not arguments against inheritance per se.
Inheritance affords a great deal of power, including substitutability
and/or the ability to override virtual functions (see Items 36 through 39,
and Exceptions below). But don't pay for what you don't need; unless you
need inheritance's power, don't endure its drawbacks.

Exceptions

Do use public inheritance to model substitutability. (See Item 37.)

Even if you don't need to provide a substitutability relationship to all callers, you do
need nonpublic inheritance if you need any of the following, in rough order from
most common (the first two points) to exceedingly rare (the rest):

If you need to override a virtual function.

If you need access to a protected member.

If you need to construct the used object before, or destroy it after, a base class.

If you need to worry about virtual base classes.

If you know you benefit from the empty base class optimization, including that it
matters in this case and that your target compiler(s) actually perform it in this
case. (See Item 8.)

If you need controlled polymorphism. That is, if you need a substitutability rela-
tionship, but that relationship should be visible only to selected code (via
friendship).

About the Authors

Herb Sutter is the author of three highly acclaimed books,
Exceptional C++ Style, Exceptional C++, and
More Exceptional C++ (Addison-Wesley). He chairs the ISO C++
standards committee, and is contributing editor and columnist for
C/C++ Users Journal. As a software architect for Microsoft,
Sutter leads the design of the C++ language extensions for .NET programming.

Andrei Alexandrescu is the author of the award-winning book
Modern C++ Design (Addison-Wesley, 2001) and is a columnist for
C/C++ Uses Journal.