Today I received something rather remarkable: a copy of my
More Effective C++ from its thirtieth print run.
So many printings is uncommon for any technical book, but it’s particularly surprising
for this one, because our understanding of what it means to apply C++
effectively has undergone considerable change since the
book’s initial publication in 1996. For me, that makes a thirtieth
printing kind of a big deal. It’s also a somewhat ironic deal, because, for
many years,
More Effective C++ was a book I
wasn’t especially fond of. Only its longevity has allowed me to develop an admiration
and respect for it.
To some degree, books are to authors what children are to
parents, and just as parents should love all their children equally, I’ve
always felt guilty that I didn't have the affection for More Effective C++ as for my other C++ books, Effective C++ and Effective
STL. The reason is simple: writing More
Effective C++ was not a terribly pleasant experience.
That’s not what I anticipated when I began. Effective C++ had been a breeze to write, because
I’d built it around guidelines and examples I’d refined through my training work
with professional developers. For that book, I was so sure about what I wanted
to say and how I wanted to say it, the manuscript nearly wrote itself. I
probably spent more time worrying about formatting than content. Furthermore, I
was excited about the prospect of becoming an author, so even the parts of the
project that felt like work were fun and exciting. It was hard work, sure, but
it was fun, exciting hard work.
Effective C++ was well
received, and being the author of a hit was also fun. I became a columnist for C++ Report, which meant I acquired a soapbox from
which to pontificate, and what’s not fun about pontificating? It was fun, fun,
fun, all the time, and that was my mindset as I set about work on More Effective C++. It was going to be the fun sequel!
As best I can remember, I produced a draft manuscript without
difficulty, and this was sent out for review. That’s when the fun ended. The book,
it became apparent, had two problems. First, it was not technically sound.
Second, it was irritating to read. One of the reviewers stopped commenting after
a few dozen pages, and, having savaged what he’d seen, concluded with “It doesn’t get any better after that.” Ouch.
So much for fun.
There’s no point in asking for comments on a manuscript if
you’re going to ignore them. The technical shortcomings of the material were
easy to verify. What’s wrong is simply wrong. The comments on my writing style
were more difficult to accept. However, these were remarks from people I
respected, and if they were willing to take the time to warn me that my prose
was more likely to enrage readers than engage them, I owed it to them (and to
my future readers) to assume that what they saw was really there. I set the
manuscript aside for a few days so I could look at it anew...and so I could prepare
for the ordeal.
With my reviewers’ comments in mind, a fresh look clarified
things. The manuscript was somewhere between bad and awful, and the reason was obvious.
While working on it, I’d been thinking of fun, fun, and more fun. I was having
fun writing, and I wanted my readers to have fun reading. But readers don’t
turn to a book on C++ for entertainment. They turn to it for information and
insight. I’d forgotten that, and it showed in my writing.
I started over.
“This is not a project about fun,” I told myself, “this is a
project about useful information for professional C++ developers who have jobs
to accomplish. Working on the book means working.
There can be some fun along the way, but the essence of the project is careful work
yielding practical insights into C++ and its effective application.” With that
in mind, I rewrote the book, and the result (after a round of substantially
less brutal reviews and the always-necessary copy-editing pass) was what was
ultimately published.
Even setting aside that I wrote
More
Effective C++ twice, working on it was bound to be harder than
Effective C++. It wasn’t my first book, so the “Oooh!
I’m going to be an author!” factor was missing. I’d put all my best and most
established material in
Effective C++, so coming
up with quality content was a bigger challenge. Furthermore, I’d changed the
format of the book. Whereas
Effective C++
consists of guidelines that can be explained and justified in a few pages, in
More Effective C++ I wanted to do that plus explore
some meatier topics
—topics that could not be covered in
such a format.
More Effective C++ is
essentially two books stapled together: one containing
Effective C++-like guidelines, and another consisting
of technical essays on topics like smart pointers, reference counting, and
proxy classes. The result is about 50% longer than the first edition of
Effective C++—another reason why
More Effective C++ called for more effort.
The work was demanding, and I really did treat it as work,
but I decided to allow myself one luxury—one Item motivated not by any
practical consideration or compelling use case, but solely by my interest in
it. One Item for fun. That Item is number 31: “Making functions virtual with
respect to more than one object.” The general term for the topic is
multi-methods. It’s sometimes also referred to as double dispatch or multiple
dispatch, though these latter terms conflate what with how. (What we want is multi-methods. Double or
multiple dispatch is how we can
implement them. I’m chagrined to admit that among those who have failed to
distinguish what from how is me: in More Effective C++,
I describe multi-methods, double dispatch, and multiple dispatch as synonyms.)
Curiously, my “fun” Item has emerged as one of the most frequently
referenced in the book. Over the years, authors have published increasingly
sophisticated ways to attack the problem I explored, often using the same
example I employed (handling collisions between objects in a video game). In
the “Interesting Comments” section of my online
errata list for More Effective C++, I refer to six books, articles, or online
discussion threads that address the implementation of multi-methods. In 1998, I
was even an outside reader on a doctoral dissertation focusing on the implementation
of multi-methods in strongly-typed programming languages.
Also curious is the turn taken by Items 28 and 29, which
together examine how non-intrusive reference-counting smart pointers can be
implemented. Experience with the code I published motivated me later to advise
developers to steer clear of custom code in favor of proven implementations,
notably Boost’s shared_ptr
(which became the basis
for TR1’s std::tr1::shared_ptr
, and, ultimately, for
C++11’s std::shared_ptr
). In Effective STL, I wrote:
The STL itself contains no reference-counting smart
pointer, and writing a good one—one that works correctly all the time—is tricky
enough that you don't want to do it unless you have to. I published the code
for a reference-counting smart pointer in More
Effective C++ in 1996, and despite basing it on established smart
pointer implementations and submitting it to extensive pre-publication
reviewing by experienced developers, a small parade of valid bug reports has
trickled in for years. The number of subtle ways in which reference-counting
smart pointers can fail is remarkable.
Remembering that I wrote this is easy, because it was quoted
in the
proposal to the C++ standardization committee that ultimately led to the
introduction of
shared_ptr
(and
weak_ptr
) into TR1. That’s the kind of thing that warms a
fellow’s heart: his inability to publish correct code being used as an argument
to expand the standard library.
Compensating somewhat is the recognition that the advice I
proffer in Item 33 (“Make non-leaf classes abstract”) has now largely become
accepted wisdom in the C++ community. At the time, it ran counter to widespread
thinking about object-oriented programming, even though I’d spoken with enough
experienced C++ library designers to know that well-engineered libraries generally
hewed to the line I advocated. I braced for a backlash from readers aggrieved
that I was telling them to abandon the idea that adding a class to a hierarchy
was a simple matter of finding a suitable base class and inheriting from it. In
fact, my advice was consistent with that idea. I simply imposed a constraint on
what it meant to be suitable, and that constraint flew in the face of what was
at the time considered a cornerstone of object-oriented programming. That Item
may be the one I’m most pleased with, because it provides a technical
underpinning for a counterintuitive, yet pervasive, constraint on good hierarchy
design.
More Effective C++ was one
of the first books to include advice on programming with exceptions, and the
essence of that guidance is all but enshrined in the Exception-Aware
Programming Hall of Fame: use RAII to manage object lifetimes, prevent
exceptions from leaving destructors, catch exceptions by reference, be aware of
the costs that exception-based error handling may incur, etc. Interestingly,
the term “RAII” doesn’t occur in the book, but the acknowledgments explain why:
The notion of using destructors to prevent resource leaks
(used in Item 9) comes from section 15.3 of Margaret A. Ellis’ and Bjarne
Stroustrup’s The Annotated C++ Reference Manual.
There the technique is called resource
acquisition is initialization. Tom Cargill suggested I shift the focus of
the approach from resource acquisition to resource release.
Thanks to Tom’s advice, Item 9 is entitled “Use destructors
to prevent resource leaks”—the essence of RAII. By the time I wrote the third
edition of
Effective C++ nearly a decade later,
I’d adopted “Use objects to manage resources” as my wording for this kind of
guideline, but I’m gratified to see that the exception-related information in
More Effective C++ was on target three years before
the appearance of what I consider the breakthrough book on programming with exceptions,
Herb Sutter’s
Exceptional C++.
In fact, viewing the Item title summary that is More Effective C++’s table of contents in this, the
dawn of the C++11 era, I’m pleased to see that, fundamentally, all the advice
there remains appropriate. Naturally, I’d tweak some things. For example, in
light of the utility of some template metaprogramming techniques that I (and
pretty much everybody else) was unaware of in 1995, I’d soften Item 7’s “Never
overload &&
, ||
,
or ,
” to something more like “Be wary of
overloading &&
, ||
,
and ,
”. Because exception specifications are
deprecated in C++11, I’d replace Item 14’s “Use exception specifications
judiciously” with something closer to “Prefer noexcept
to exception specifications.” Such tweaks notwithstanding, the high-level
advice in More Effective C++ suffers from a lot
less decay than one might expect from a book in its seventeenth year. I can’t
help but be a little proud of that.
The thirtieth printing of
More
Effective C++ didn’t produce the same book as the first one. As with all
my books, I solicit bug reports and other improvement suggestions from my readers,
and I keep the book’s
errata list
online, so everyone can see the problems that have been reported and the ones I
have fixed. For each new printing, I do my best to revise the book to address outstanding
issues, but sometimes my schedule is such that there’s not time to do it. (The
window between being notified of a forthcoming printing and the deadline for
revisions is typically rather short.) The 30 printings of
More Effective C++ since 1996 have yielded 20 different versions.
More than 20 versions have actually been printed, because I’m counting
only domestic printings. The book has been printed in alternative forms for
English-speaking markets outside the United States (e.g., with a different
cover design and less expensive paper for markets where North American pricing is
inappropriate). It’s also been translated into a number of foreign languages,
including German, Japanese, Korean, Polish, Russian, and both traditional and
simplified Chinese. All in all,
More Effective C++ has
been printed more than 150,000 times in at least ten languages. It’s available
in a number of electronic formats, as well.
|
My collection of More Effective C++es. Domestic printings are on the left, international versions on the right. Detail-oriented readers will notice 31 copies of the domestic version. That's because there were two versions of the third printing. |
My little less-loved book has
surpassed any reasonable expectations an author could have. Still quietly
chugging away in its seventeeth year, still providing useful information to C++
software developers—what more could I ask? As I said, I’ve grown to admire and
respect this book—and even to have a parent’s affection for it.