C++ Coding Standards
The C++ In-Depth Series
Bjarne Stroustrup, Editor
"I havemade this letter longer than usual, because I lack the time to make it short."
—BLAISE PASCAL
he advent of the ISO/ANSI C++ standard marked the beginning of a new era for C++
programmers. The standard offers many new facilities and opportunities, but how can a
real-world programmer find the time to discover the key nuggets of wisdom within this mass
of information? The C++ In-Depth Series minimizes learning time and confusion by giving
programmers concise, focused guides to specific topics.
Each book in this series presents a single topic, at a technical level appropriate to that topic.
The Series' practical approach is designed to lift professionals to their next level of
programming skills. Written by experts in the field, these short, in-depth monographs can be
read and referenced without the distraction of unrelated material. The books are
cross-referenced within the Series, and also reference The C++ Programming Language by
Bjarne Stroustrup.
As you develop your skills in C++, it becomes increasingly important to separate essential
information from hype and glitz, and to find the in-depth content you need in order to grow.
The C++ In-Depth Series provides the tools, concepts, techniques, and new approaches to
C++ that will give you a critical edge.
Titles in the Series
Accelerated C++: Practical Programming by Example, Andrew Koenig and Barbara E. Moo
Applied C++: Practical Techniques for Building Better Software, Philip Romanik and AmyMuntz
The Boost Graph Library: User Guide and Reference Manual, JeremyG. Siek, Lie-Quan Lee, and Andrew
Lumsdaine
C++ Coding Standards: 101 Rules, Guidelines, and Best Practices, Herb Sutter and Andrei Alexandrescu
C++ In-Depth Box Set, Bjarne Stroustrup, Andrei Alexandrescu, Andrew Koenig, Barbara E. Moo,
Stanley B. Lippman, and Herb Sutter
C++ Network Programming, Volume 1: Mastering Complexity with ACE and Patterns, Douglas C. Schmidt
and Stephen D. Huston
C++ Network Programming, Volume 2: Systematic Reuse with ACE and Frameworks, Douglas C. Schmidt
and Stephen D. Huston
C++ TemplateMetaprogramming: Concepts, Tools, and Techniques fromBoost and Beyond, David Abrahams
and Aleksey Gurtovoy
Essential C++, Stanley B. Lippman
Exceptional C++: 47 Engineering Puzzles, Programming Problems, and Solutions, Herb Sutter
Exceptional C++ Style: 40 New Engineering Puzzles, Programming Problems, and Solutions, Herb Sutter
Modern C++ Design: Generic Programming and Design Patterns Applied, Andrei Alexandrescu
More Exceptional C++: 40 New Engineering Puzzles, Programming Problems, and Solutions, Herb Sutter
For more information, check out the series web site at www.awprofessional.com/series/indepth/
T
C++ Coding Standards
101 Rules, Guidelines, and Best Practices
Herb Sutter
Andrei Alexandrescu
The authors and publisher have taken care in the preparation of this book, but make no expressed or implied
warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for
incidental or consequential damages in connection with or arising out of the use of the information or pro-grams
contained herein.
Publisher: John Wait Editor in Chief: Don
O'Hagan Acquisitions Editor: Peter Gordon
Editorial Assistant: Kim Boedigheimer
Marketing Manager: Chanda Leary-Coutu
Cover Designer: Chuti Prasertsith
Managing Editor: John Fuller Project Editor:
Lara Wysong Copy Editor: Kelli Brooks
Manufacturing Buyer: Carol Melville
The publisher offers excellent discounts on this book when ordered in quantity for bulk purchases or special
sales, which may include electronic versions and /or custom covers and content particular to your business,
training goals, marketing focus, and branding interests. For more information, please contact:
U. S. Corporate and Government Sales
(800) 382-3419
corpsales@pearsontechgroup.com
For sales outside the U. S., please contact:
International Sales
international@pearsoned.com
Visit us on the Web: www.awprofessional.com
Library of Congress Cataloging-in-Publication Data:
Sutter, Herb.
C++ coding standards : 101 rules, guidelines, and best practices / Herb Sutter, Andrei Alexandrescu.
p. cm.
Includes bibliographical references and index. ISBN 0-321-11358-6
(pbk.: alk. paper) C++ (Computer program language) I. Alexandrescu,
Andrei. II. Title.
QA76.73.C153S85 2004
005.13'3—dc22
2004022605
Copyright © 2005 Pearson Education, Inc.
All rights reserved. Printed in the United States of America. This publication is protected by copyright, and
permission must be obtained from the publisher prior to any prohibited reproduction, storage in a retrieval
system, or transmission in any form or by any means, electronic, mechanical, photocopying, recording, or
likewise. For information regarding permissions, write to:
Pearson Education, Inc.
Rights and Contracts Department
One Lake Street
Upper Saddle River, NJ 07458
ISBN0-32-111358-6
Text printed in the United States on recycled paper at Courier in Stoughton, Massachusetts.
First printing, October 2004
For the millions of current C++ programmers
Contents
Preface xi
Organizational and Policy Issues 1
0. Don't sweat the small stuff. (Or: Know what not to standardize.) 2
1. Compile cleanly at high warning levels. 4
2. Use an automated build system. 7
3. Use a version control system. 8
4. Invest in code reviews. 9
Design Style 11
5. Give one entity one cohesive responsibility. 12
6. Correctness, simplicity, and clarity come first. 13
7. Know when and how to code for scalability. 14
8. Don't optimize prematurely. 16
9. Don't pessimize prematurely. 18
10. Minimize global and shared data. 19
11. Hide information. 20
12. Know when and how to code for concurrency. 21
13. Ensure resources are owned by objects. Use explicit RAII and smart pointers. 24
Coding Style 27
14. Prefer compile- and link-time errors to run-time errors. 28
15. Use const proactively. 30
16. Avoid macros. 32
VII
vii i Contents
17. Avoid magic numbers. 34
18. Declare variables as locally as possible. 35
19. Always initialize variables. 36
20. Avoid long functions. Avoid deep nesting. 38
21. Avoid initialization dependencies across compilation units. 39
22. Minimize definitional dependencies. Avoid cyclic dependencies. 40
23. Make header files self-sufficient. 42
24. Always write internal #include guards. Never write external #include guards. 43
Functions and Operators 45
25. Take parameters appropriately by value, (smart) pointer, or reference. 46
26. Preserve natural semantics for overloaded operators. 47
27. Prefer the canonical forms of arithmetic and assignment operators. 48
28. Prefer the canonical form of + + and --. Prefer calling the prefix forms. 50
29. Consider overloading to avoid implicit type conversions. 51
30. Avoid overloading &&, ||, or , (comma). 52
31. Don't write code that depends on the order of evaluation of function
arguments. 54
Class Design and Inheritance 55
32. Be clear what kind of class you're writing. 56
33. Prefer minimal classes to monolithic classes. 57
34. Prefer composition to inheritance. 58
35. Avoid inheriting from classes that were not designed to be base classes. 60
36. Prefer providing abstract interfaces. 62
37. Public inheritance is substitutability. Inherit, not to reuse, but to be reused. 64
38. Practice safe overriding. 66
39. Consider making virtual functions nonpublic, and public functions nonvirtual. 68
40. Avoid providing implicit conversions. 70
41. Make data members private, except in behaviorless aggregates (C-style
structs). 72
42. Don't give away your internals. 74
43. Pimpl judiciously. 76
44. Prefer writing nonmember nonfriend functions. 79
45. Always provide new and delete together. 80
46. If you provide any class-specific new, provide all of the standard forms (plain,
in-place, and nothrow). 82
Contents ix
Construction, Destruction, and Copying 85
47. Define and initialize member variables in the same order. 86
48. Prefer initialization to assignment in constructors. 87
49. Avoid calling virtual functions in constructors and destructors. 88
50. Make base class destructors public and virtual, or protected and nonvirtual. 90
51. Destructors, deallocation, and swap never fail. 92
52. Copy and destroy consistently. 94
53. Explicitly enable or disable copying. 95
54. Avoid slicing. Consider Clone instead of copying in base classes. 96
55. Prefer the canonical form of assignment. 99
56. Whenever it makes sense, provide a no-fail swap (and provide it correctly). 100
Namespaces and Modules 103
57. Keep a type and its nonmember function interface in the same namespace. 104
58. Keep types and functions in separate namespaces unless they're specifically
intended to work together. 106
59. Don't write namespace usings in a header file or before an #include. 108
60. Avoid allocating and deallocating memory in different modules. Ill
61. Don't define entities with linkage in a header file. 112
62. Don't allow exceptions to propagate across module boundaries. 114
63. Use sufficiently portable types in a module's interface. 116
Templates and Genericity 119
64. Blend static and dynamic polymorphism judiciously. 120
65. Customize intentionally and explicitly. 122
66. Don't specialize function templates. 126
67. Don't write unintentionally nongeneric code. 128
Error Handling and Exceptions 129
68. Assert liberally to document internal assumptions and invariants. 130
69. Establish a rational error handling policy, and follow it strictly. 132
70. Distinguish between errors and non-errors. 134
71. Design and write error-safe code. 137
72. Prefer to use exceptions to report errors. 140
73. Throw by value, catch by reference. 144
74. Report, handle, and translate errors appropriately. 145
75. Avoid exception specifications. 146
Contents
STL: Containers 149
76. Use vector by default. Otherwise, choose an appropriate container. 150
77. Use vector and string instead of arrays. 152
78. Use vector (and string::c_str) to exchange data with non-C++ APIs. 153
79. Store only values and smart pointers in containers. 154
80. Prefer push_back to other ways of expanding a sequence. 155
81. Prefer range operations to single-element operations. 156
82. Use the accepted idioms to really shrink capacity and really erase elements. 157
STL: Algorithms 159
83. Use a checked STL implementation. 160
84. Prefer algorithm calls to handwritten loops. 162
85. Use the right STL search algorithm. 165
86. Use the right STL sort algorithm. 166
87. Make predicates pure functions. 168
88. Prefer function objects over functions as algorithm and comparer arguments.
170
89. Write function objects correctly. 172
Type Safety 173
90. Avoid type switching; prefer polymorphism. 174
91. Rely on types, not on representations. 176
92. Avoid using reinterpret_cast. 177
93. Avoid using static_cast on pointers. 178
94. Avoid casting away const. 179
95. Don't use C-style casts. 180
96. Don't memcpy or memcmp non-PODs. 182
97. Don't use unions to reinterpret representation. 183
98. Don't use varargs (ellipsis). 184
99. Don't use invalid objects. Don't use unsafe functions. 185
100.Don't treat arrays polymorphically. 186
Bibliography 187
Summary of Summaries 195
Index 209
Preface
Get into a rut early: Do the same process the same way. Accumulate idioms.
Standardize. The only differenced) between Shakespeare and you was the
size of his idiom list—not the size of his vocabulary.
—Alan Perlis [emphasis ours]
The best thing about standards is that there are so many to choose from.
—Variously attributed
We want to provide this book as a basis for your team's coding standards for two
principal reasons:
• A coding standard should reflect the community's best tried-and-true experience: It
should contain proven idioms based on experience and solid understanding of
the language. In particular, a coding standard should be based firmly on the ex
tensive and rich software development literature, bringing together rules,
guidelines, and best practices that would otherwise be left scattered throughout
many sources.
• Nature abhors a vacuum: If you don't consciously set out reasonable rules, usually
someone else will try to push their own set of pet rules instead. A coding stan
dard made that way usually has all of the least desirable properties of a coding
standard; for example, many such standards try to enforce a minimalistic C-style
use of C++.
Many bad coding standards have been set by people who don't understand the lan-guage
well, don't understand software development well, or try to legislate too
much. A bad coding standard quickly loses credibility and at best even its valid
guidelines are liable to be ignored by disenchanted programmers who dislike or
disagree with its poorer guidelines. That's "at best"—at worst, a bad standard might
actually be enforced.
xi
XII Preface
How to Use This Book
Think. Do follow good guidelines conscientiously; but don't follow them blindly. In
this book's Items, note the Exceptions clarifying the less common situations where
the guidance may not apply. No set of guidelines, however good (and we think
these ones are), should try to be a substitute for thinking.
Each development team is responsible for setting its own standards, and for setting
them responsibly. That includes your team. If you are a team lead, involve your
team members in setting the team's standards; people are more likely to follow
standards they view as their own than they are to follow a bunch of rules they feel are
being thrust upon them.
This book is designed to be used as a basis for, and to be included by reference in,
your team's coding standards. It is not intended to be the Last Word in coding stan-dards,
because your team will have additional guidelines appropriate to your par-ticular
group or task, and you should feel free to add those to these Items. But we
hope that this book will save you some of the work of (re)developing your own, by
documenting and referencing widely-accepted and authoritative practices that apply
nearly universally (with Exceptions as noted), and so help increase the quality and
consistency of the coding standards you use.
Have your team read these guidelines with their rationales (i.e., the whole book, and
selected Items' References to other books and papers as needed), and decide if there
are any that your team simply can't live with (e.g., because of some situation unique to
your project). Then commit to the rest. Once adopted, the team's coding standards
should not be violated except after consulting with the whole team.
Finally, periodically review your guidelines as a team to include practical experience
and feedback from real use.
Coding Standards and You
Good coding standards can offer many interrelated advantages:
• Improved code quality: Encouraging developers to do the right things in a consis
tent way directly works to improve software quality and maintainability.
• Improved development speed: Developers don't need to always make decisions
starting from first principles.
• Better teamwork: They help reduce needless debates on inconsequential issues
and make it easier for teammates to read and maintain each other's code.
• Uniformity in the right dimension: This frees developers to be creative in directions
that matter.
Preface XIII
Under stress and time pressure, people do what they've been trained to do. They fall
back on habit. That's why ER units in hospitals employ experienced, trained per-sonnel;
even knowledgeable beginners would panic.
As software developers, we routinely face enormous pressure to deliver tomorrow's
software yesterday. Under schedule pressure, we do what we are trained to do and
are used to doing. Sloppy programmers who in normal times don't know good prac-tices
of software engineering (or aren't used to applying them) will write even sloppier
and buggier code when pressure is on. Conversely, programmers who form good
habits and practice them regularly will keep themselves organized and deliver quality
code, fast.
The coding standards introduced by this book are a collection of guidelines for writing
high-quality C++ code. They are the distilled conclusions of a rich collective ex-perience
of the C++ community. Much of this body of knowledge has only been
available in bits and pieces spread throughout books, or as word-of-mouth wisdom.
This book's intent is to collect that knowledge into a collection of rules that is terse,
justified, and easy to understand and follow.
Of course, one can write bad code even with the best coding standards. The same is
true of any language, process, or methodology. A good set of coding standards fosters
good habits and discipline that transcend mere rules. That foundation, once acquired,
opens the door to higher levels. There's no shortcut; you have to develop vocabulary
and grammar before writing poetry. We just hope to make that easier.
We address this book to C++ programmers of all levels:
If you are an apprentice programmer, we hope you will find the rules and their ra-tionale
helpful in understanding what styles and idioms C++ supports most natu-rally.
We provide a concise rationale and discussion for each rule and guideline to
encourage you to rely on understanding, not just rote memorization.
For the intermediate or advanced programmer, we have worked hard to provide a
detailed list of precise references for each rule. This way, you can do further research
into the rule's roots in C++'s type system, grammar, and object model.
At any rate, it is very likely that you work in a team on a complex project. Here is
where coding standards really pay off—you can use them to bring the team to a
common level and provide a basis for code reviews.
About This Book
We have set out the following design goals for this book:
• Short is better than long: Huge coding standards tend to be ignored; short ones get
read and used. Long Items tend to be skimmed; short ones get read and used.
XIV Preface
• Each Item must be noncontroversial: This book exists to document widely agreed-upon
standards, not to invent them. If a guideline is not appropriate in all cases,
it will be presented that way (e.g., "Consider X..." instead of "Do X...") and we
will note commonly accepted exceptions.
• Each Item must be authoritative: The guidelines in this book are backed up by ref
erences to existing published works. This book is intended to also provide an
index into the C++ literature.
• Each Item must need sax/ing: We chose not to define new guidelines for things that
you'll do anyway, that are already enforced or detected by the compiler, or that
are already covered under other Items.
Example: "Don't return a pointer/reference to an automatic variable" is a
good guideline, but we chose not to include it in this book because all of the
compilers we tried already emit a warning for this, and so the issue is al-ready
covered under the broader Item 1, "Compile cleanly at high warning
levels."
Example: "Use an editor (or compiler, or debugger)" is a good guideline, but
of course you'll use those tools anyway without being told; instead, we
spend two of our first four Items on "Use an automated build system" and
"Use a version control system."
Example: "Don't abuse goto" is a great Item, but in our experience pro-grammers
universally know this, and it doesn't need saying any more.
Each Item is laid out as follows:
• Item title: The simplest meaningful sound bite we could come up with as a mne
monic for the rule.
• Summary: The most essential points, briefly stated.
• Discussion: An extended explanation of the guideline. This often includes brief
rationale, but remember that the bulk of the rationale is intentionally left in the
References.
• Examples (if applicable): Examples that demonstrate a rule or make it memorable.
• Exceptions (if applicable): Any (and usually rare) cases when a rule doesn't apply.
But beware the trap of being too quick to think: "Oh, I'm special; this doesn't
apply in my situation"—that rationalization is common, and commonly wrong.
• References: See these parts of the C++ literature for the full details and analysis.
In each section, we chose to nominate a "most valuable Item." Often, it's the first
Item in a section, because we tried to put important Items up front in each part; but
Preface xv
other times an important Item couldn't be put up front, for flow or readability reasons,
and we felt the need to call it out for special attention in this way.
Acknowledgments
Many thanks to series editor Bjarne Stroustrup, to editors Peter Gordon and Debbie
Lafferty, and to Tyrrell Albaugh, Kim Boedigheimer, John Fuller, Bernard Gaffney,
Curt Johnson, Chanda Leary-Coutu, Charles Leddy, Heather Mullane, Chuti
Prasertsith, Lara Wysong, and the rest of the Addison-Wesley team for their assis-tance
and persistence during this project. They are a real pleasure to work with.
Inspiration for some of the "sound bites" came from many sources, including the
playful style of [Cline99], the classic import this of [Peters99], and the legendary and
eminently quotable Alan Perlis.
We especially want to thank the people whose technical feedback has helped to
make many parts of this book better than they would otherwise have been. Series
editor Bjarne Stroustrup's incisive comments from concept all the way through to
the final draft were heavily influential and led to many improvements. We want to
give special thanks to Dave Abrahams, Marshall Cline, Kevlin Henney, Howard
Hinnant, Jim Hyslop, Nicolai Josuttis, Jon Kalb, Max Khesin, Stan Lippman, Scott
Meyers, and Daveed Vandevoorde for their active participation in review cycles and
detailed comments on several drafts of this material. Other valuable comments and
feedback were contributed by Chuck Allison, Samir Bajaj, Marc Barbour, Damian
Dechev, Steve Dewhurst, Peter Dimov, Alan Griffiths, Michi Henning, James Kanze,
Matt Marcus, Petru Marginean, Robert C. "Uncle Bob" Martin, Jeff Peil, Peter
Pirkel-bauer, Vladimir Prus, Dan Saks, Luke Wagner, Matthew Wilson, and Leor
Zolman.
As usual, the remaining errors, omissions, and shameless puns are ours, not theirs.
Herb Sutter
Andrei Alexandrescu
Seattle, September 2004
Organizational and
Policy Issues
If builders built buildings the way programmers wrote programs, then
the first woodpecker that came along would destroy civilization.
—GeraldWeinberg
In the grand tradition of C and C++, we count the zero-based way. The prime directive,
Item 0, covers what we feel is the most basic advice about coding standards.
The rest of this introductory section goes on to target a small number of carefully se-lected
basic issues that are mostly not directly about the code itself, but on essential
tools and techniques for writing solid code.
Our vote for the most valuable Item in this section goes to Item 0: Don't sweat the
small stuff. (Or: Know what not to standardize.)
C++ Coding Standards
0. Don't sweat the small stuff.
(Or: Know what not to standardize.)
Summary
Say only what needs saying: Don't enforce personal tastes or obsolete practices.
Discussion
Issues that are really just personal taste and don't affect correctness or readability
don't belong in a coding standard. Any professional programmer can easily read and
write code that is formatted a little differently than they're used to.
Do use consistent formatting within each source file or even each project, because it's
jarring to jump around among several styles in the same piece of code. But don't try to
enforce consistent formatting across multiple projects or across a company.
Here are several common issues where the important thing is not to set a rule but just
to be consistent with the style already in use within the file you're maintaining:
• Don't specify how much to indent, but do indent to show structure: Use any number
of spaces you like to indent, but be consistent within at least each file.
• Don't enforce a specific line length, but do keep line lengths readable: Use any length
of line you like, but don't be excessive. Studies show that up to ten-word text
widths are optimal for eye tracking.
• Don't overlegislate naming, but do use a consistent naming convention: There are only
two must-dos: a) never use "underhanded names," ones that begin with an un
derscore or that contain a double underscore; and b) always use
ONLY_UPPERCASE_NAMES for macros and never think about writing a macro
that is a common word or abbreviation (including common template parame
ters, such as T and U; writing #define T anything is extremely disruptive). Oth
erwise, do use consistent and meaningful names and follow a file's or module's
convention. (If you can't decide on your own naming convention, try this one:
Name classes, functions, and enums LikeThis; name variables likeThis; name
private member variables likeThis_; and name macros LIKE_THIS.)
• Don't prescribe commenting styles (except where tools extract certain styles into docu
mentation), but do write useful comments: Write code instead of comments where
possible (e.g., see Item 16). Don't write comments that repeat the code; they get
out of sync. Do write illuminating comments that explain approach and rationale.
Finally, don't try to enforce antiquated rules (see Examples 3 and 4) even if they once
appeared in older coding standards.
Examples
Example 1: Brace placement. There is no readability difference among:
Organizational and Policy Issues 3
void using_k_and_r_style() {
void putting_each_brace_on_its_own_line()
void or_putting_each_brace_onjts_own_line_indented()
Any professional programmer can easily read and write any of these styles without
hardship. But do be consistent: Don't just place braces randomly or in a way that ob-scures
scope nesting, and try to follow the style already in use in each file. In this
book, our brace placement choices are motivated by maximizing readability within
our editorial constraints.
Example 2: Spaces vs. tabs. Some teams legitimately choose to ban tabs (e.g.,
[BoostLRG]), on the grounds that tabs vary from editor to editor and, when mis-used,
turn indenting into outdenting and nondenting. Other equally respectable teams
legitimately allow tabs, adopting disciplines to avoid their potential draw-backs. Just
be consistent: If you do allow tabs, ensure it is never at the cost of code clarity and
readability as team members maintain each other's code (see Item 6). If you don't
allow tabs, allow editors to convert spaces to tabs when reading in a source file so
that users can work with tabs while in the editor, but ensure they con-vert the tabs
back to spaces when writing the file back out.
Example 3: Hungarian notation. Notations that incorporate type information in
vari-able names have mixed utility in type-unsafe languages (notably C), are possible
but have no benefits (only drawbacks) in object-oriented languages, and are
impossible in generic programming. Therefore, no C++ coding standard should
require Hun-garian notation, though a C++ coding standard might legitimately
choose to ban it.
Example 4: Single entry, single exit ("SESE"). Historically, some coding standards have
required that each function have exactly one exit, meaning one return statement.
Such a requirement is obsolete in languages that support exceptions and destructors,
where functions typically have numerous implicit exits. Instead, follow standards
like Item 5 that directly promote simpler and shorter functions that are inherently
easier to understand and to make error-safe.
References
[BoostLRG] • [Brooks95] §12 • [Constantine95] §29 • [Keffer95] p. 1 • [Kernighan99] §1.1,
§1.3, §1.6-7 • [Lakos96] §1.4.1, §2.7 • [McConnell93] §9, §19 • [Stroustrup94] §4.2-3 •
[StroustrupOO] §4.9.3, §6.4, §7.8, §C.l • [SutterOO] §6, §20 • [SuttHyslOl]
C++ Coding Standards
1. Compile cleanly at high warning levels.
Summary
Take warnings to heart: Use your compiler's highest warning level. Require clean
(warning-free) builds. Understand all warnings. Eliminate warnings by changing
your code, not by reducing the warning level.
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):
//File: myproj/myjambda.h -- wraps Boost's lambda.hpp
// Always include this file; don't use lambda.hpp directly.
// NOTE: Our build now automatically checks "grep lambda.hpp <srcfile>".
// Boost.Lambda produces noisy compiler warnings that we know are innocuous.
// When they fix it we'll remove the pragmas below, but this header will still exist.
/ /
#pragma warning(push) //disable for this header only
#pragma warning(disable:4512)
#pragma warning(disable:4180)
#include <boost/lambda/lambda.hpp> #pragma
warning(pop) //restore original warning level
Organizational and Policy Issues
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:
//... inside a user-defined allocator that has no use for the hint...
// warning: "unused parameter 'localityHint'"
pointer allocate( sizejype numObjects, const void *localityHint = 0 )
{ return static_cast<pointer>( mallocShared( numObjects * sizeof(T))); }
// new version: eliminates warning
pointer allocate( sizejype numObjects, const void * /* localityHint */ = 0 ) {
return static_cast<pointer>( mallocShared( numObjects * sizeof(T)) ); }
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):
// warning: "variable lock' is defined but never used"
void Fun() { Lock lock;
}
// new version: probably eliminates warning
void Fun() {
Lock lock;
lock;
}
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
C++ Coding Standards
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):
//warning: missing "return"
int Fun( Color c ) {
switch( c) {
case Red: return 2;
case Green: return 0;
case Blue:
case Black: return 1;
}
}
// new version: eliminates warning int
Fun( Color c ) {
switch( c) {
case Red: return 2;
case Green: return 0;
case Blue:
case Black: return 1;
default: assert( {"should never get here!" );
return -1;
}
}
// /"string" evaluates to false
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
[Meyers97] §48 • [Stroustrup94] §2.6.2
Organizational and Policy Issues
2. Use an automated build system.
Summary
Push the (singular) button: Use a fully automatic ("one-action") build system that
builds the whole project without user intervention.
Discussion
A one-action build process is essential. It must produce a dependable and repeatable
translation of your source files into a deliverable package. There is a broad range of
automated build tools available, and no excuse not to use one. Pick one. Use it.
We've seen organizations that neglect the "one-action" requirement. Some consider
that a few mouse clicks here and there, running some utilities to register
COM/CORBA servers, and copying some files by hand constitute a reasonable build
process. But you don't have time and energy to waste on something a machine can do
faster and better. You need a one-action build that is automated and dependable.
Successful builds should be silent, warning-free (see Item 1). The ideal build pro-duces
no noise and only one log message: "Build succeeded."
Have two build modes: Incremental and full. An incremental build rebuilds only
what has changed since the last incremental or full build. Corollary: The second of
two successive incremental builds should not write any output files; if it does, you
probably have a dependency cycle (see Item 22), or your build system performs un-necessary
operations (e.g., writes spurious temporary files just to discard them).
A project can have different forms of full build. Consider parameterizing your build by
a number of essential features; likely candidates are target architecture, debug vs.
release, and breadth (essential files vs. all files vs. full installer). One build setting
can create the product's essential executables and libraries, another might also create
ancillary files, and a full-fledged build might create an installer that comprises all
your files, third-party redistributables, and installation code.
As projects grow over time, so does the cost of not having an automated build. If
you don't use one fromthe start, you will waste time and resources.Worse still, by the
time the need for an automated build becomes overwhelming, you will be under more
pressure than at the start of the project.
Large projects might have a "build master" whose job is to care for the build system.
References
[Brooks95] §13, §19 • [DewhnrstO3] §1 • [GnuMake] • [StroustrupOO] §9.1
C++ Coding Standards
3. Use a version control system.
Summary
The palest of ink is better than the best memory (Chinese proverb): Use a version
control system (VCS). Never keep files checked out for long periods. Check in fre-quently
after your updated unit tests pass. Ensure that checked-in code does not
break the build.
Discussion
Nearly all nontrivial projects need more than one developer and/or take more than a
week of work. On such projects, you will need to compare historical versions of the
same file to determine when (and/or by whom) changes were introduced. You will
need to control and manage source changes.
When there are multiple developers, those developers will make changes in parallel,
possibly to different parts of the same file at the same time. You need tools to automate
checkout/versioning of file and, in some cases, merging of concurrent edits. A VCS
automates and controls checkouts, versioning, and merging. A VCS will do it faster
and more correctly than you could do it by hand. And you don't have time to fiddle
with administrivia—you have software to write.
Even a single developer has "oops!" and "huh?" moments, and needs to figure out
when and why a bug or change was introduced. So will you. A VCS automatically
tracks the history of each file and lets you "turn the clock back." The question isn't
whether you will want to consult the history, but when.
Don't break the build. The code in the VCS must always build successfully.
The broad range of VCS offerings leaves no excuse not to use one. The least expensive
and most popular is cvs (see References). It is a flexible tool, featuring TCP/IP access,
optional enhanced security (by using the secure shell ssh protocol as a back-end),
excellent administration through scripting, and even a graphical interface. Many
other VCS products either treat cvs as a standard to emulate, or build new
functionality on top of it.
Exceptions
A project with one programmer that takes about a week from start to finish probably
can live without a VCS.
References
[BetterSCM] • [Brooks95] §11, §13 • [CVS]
Organizational and Policy Issues
4. Invest in code reviews.
Summary
Re-view code: More eyes will help make more quality. Show your code, and read
others'. You'll all learn and benefit.
Discussion
A good code review process benefits your team in many ways. It can:
• Increase code quality through beneficial peer pressure.
• Find bugs, non-portable code (if applicable), and potential scaling problems.
• Foster better design and implementation through cross-breeding of ideas.
• Bring newer teammates and beginners up to speed.
• Develop common values and a sense of community inside the team.
• Increase meritocracy, confidence, motivation, and professional pride.
Many shops neither reward quality code and quality teams nor invest time and
money encouraging them. We hope we won't have to eat our words a couple of
years from now, but we feel that the tide is slowly changing, due in part to an in-creased
need for safe and secure software. Code reviews help foster exactly that, in
addition to being an excellent (and free!) method of in-house training.
Even if your employer doesn't yet support a code reviewing process, do increase
management awareness (hint: to start, show them this book) and do your best to
make time and conduct reviews anyway. It is time well spent.
Make code reviews a routine part of your software development cycle. If you agree
with your teammates on a reward system based on incentives (and perhaps disin-centives),
so much the better.
Without getting too formalistic, it's best to get code reviews in writing—a simple e-mail
can suffice. This makes it easier to track your own progress and avoid duplication.
When reviewing someone else's code, you might like to keep a checklist nearby for
reference. We humbly suggest that one good list might be the table of contents of the
book you are now reading. Enjoy!
In summary: We know we're preaching to the choir, but it had to be said. Your ego
may hate a code review, but the little genius programmer inside of you loves it be-cause
it gets results and leads to better code and stronger applications.
References
[Constantine95] §10, §22, §33 • [McConnell93] §24 • [MozillaCRFAQ]
Design Style
Fools ignore complexity. Pragmatists suffer it. Some can avoid it. Geniuses remove it.
—Alan Perlis
But I also knew, and forgot, Hoare's dictum that
premature optimization is the root of all evil in programming.
—Donald Knuth,
The Errors ofTeX [Knuth89]
It's difficult to fully separate Design Style and Coding Style.We have tried to leave to
the next section those Items that generally crop up when actually writing code.
This section focuses on principles and practices that apply more broadly than just to a
particular class or function. A classic case in point is the balance among simplicity and
clarity (Item 6), avoiding premature optimization (Item 8), and avoiding premature
pessimization (Item 9). Those three Items apply, not just at the function-coding level,
but to the larger areas of class and module design tradeoffs and to far-reaching
application architecture decisions. (They also apply to all programmers. If you think
otherwise, please reread the above Knuth quote and note its citation.)
Following that, many of the other Items in this and the following section deal with
aspects of dependency management—a cornerstone of software engineering and a
recurring theme throughout the book. Stop and think of some random good software
engineering technique—any good technique. Whichever one you picked, in one
way or another it will be about reducing dependencies. Inheritance? Make code
written to use the base class less dependent on the actual derived class. Minimize
global variables? Reduce long-distance dependencies through widely visible data.
Abstraction? Eliminate dependencies between code that manipulates concepts and
code that implements them. Information hiding? Make client code less dependent
on an entity's implementation details. An appropriate concern for dependency man-agement
is reflected in avoiding shared state (Item 10), applying information hiding
(Item 11), and much more.
Our vote for the most valuable Item in this section goes to Item 6: Correctness, sim-plicity,
and clarity come first. That they really, really must.
11
12 C++ Coding Standards
5. Give one entity one cohesive responsibility.
Summary
Focus on one thing at a time: Prefer to give each entity (variable, class, function,
namespace, module, library) one well-defined responsibility. As an entity grows, its
scope of responsibility naturally increases, but its responsibility should not diverge.
Discussion
A good business idea, they say, can be explained in one sentence. Similarly, each
program entity should have one clear purpose.
An entity with several disparate purposes is generally disproportionately harder to
use, because it carries more than the sum of the intellectual overhead, complexity,
and bugs of its parts. Such an entity is larger (often without good reason) and harder to
use and reuse. Also, such an entity often offers crippled interfaces for any of its
specific purposes because the partial overlap among various areas of functionality
blurs the vision needed for crisply implementing each.
Entities with disparate responsibilities are typically hard to design and implement.
"Multiple responsibilities" frequently implies "multiple personalities"—a combina-torial
number of possible behaviors and states. Prefer brief single-purpose functions
(see also Item 39), small single-purpose classes, and cohesive modules with clean
boundaries.
Prefer to build higher-level abstractions from smaller lower-level abstractions. Avoid
collecting several low-level abstractions into a larger low-level conglomerate. Im-plementing
a complex behavior out of several simple ones is easier than the reverse.
Examples
Example 1: realloc. In Standard C, realloc is an infamous example of bad design. It
has to do too many things: allocate memory if passed NULL, free it if passed a zero
size, reallocate it in place if it can, or move memory around if it cannot. It is not easily
extensible. It is widely viewed as a shortsighted design failure.
Example 2: basic_string. In Standard C++, std::basic_string is an equally infamous ex-ample
of monolithic class design. Too many "nice-to-have" features were added to a
bloated class that tries to be a container but isn't quite, is undecided on iteration vs.
indexing, and gratuitously duplicates many standard algorithms while leaving little
space for extensibility. (See Item 44's Example.)
References
[HenneyO2a] • [HenneyO2b] • [McConnell93] §10.5 • [StroustrupOO] §3.8, §4.9.4,
§23.4.3.1 • [SutterOO] §10, §12, §19, §23 • [SutterO2] §1 • [SutterO4] §37-40
Design Style 13
6. Correctness, simplicity, and clarity come first.
Summary
KISS (Keep It Simple Software): Correct is better than fast. Simple is better than
complex. Clear is better than cute. Safe is better than insecure (see Items 83 and 99).
Discussion
It's hard to overstate the value of simple designs and clear code. Your code's maintainer
will thank you for making it understandable—and often that will be your future self, try-ing
to remember what you were thinking six months ago. Hence such classic wisdom as:
Programs must be written for people to read, and only incidentally for machines to
execute. —Harold Abelson and Gerald Jay Sussman
Write programs for people first, computers second. —Steve McConnell
The cheapest, fastest and most reliable components of a computer system are those
that aren't there. —Gordon Bell
Those missing components are also the most accurate (they never make mistakes),
the most secure (they can't be broken into), and the easiest to design, document,
test and maintain. The importance of a simple design can't be overemphasized.
—Jon Bentley
Many of the Items in this book naturally lead to designs and code that are easy to
change, and clarity is the most desirable quality of easy-to-maintain, easy-to-refactor
programs. What you can't comprehend, you can't change with confidence.
Probably the most common tension in this area is between code clarity and code opti-mization
(see Items 7, 8, and 9). When—not if—you face the temptation to optimize
prematurely for performance and thereby pessimize clarity, recall Item 8's point: It is
far, far easier to make a correct program fast than it is to make a fast program correct.
Avoid the language's "dusty corners." Use the simplest techniques that are effective.
Examples
Example 1: Avoid gratuitous/clever operator overloading. One needlessly weird GUI library
had users write w + c; to add a child control c to a widget w. (See Item 26.)
Example 2: Prefer using named variables, not temporaries, as constructor parameters. This
avoids possible declaration ambiguities. It also often makes the purpose of your
code clearer and thus is easier to maintain. It's also often safer (see Items 13 and 31).
References
[Abelson96] • [BentleyOO] §4 • [Cargill92] pp. 91-93 • [Cline99] §3.05-06 • [Constantine95]
§29 • [Keffer95] p. 17 • [Lakos96] §9.1, §10.2.4 • [McConnell93] • [MeyersOl] §47 •
[StroustrupOO] §1.7, §2.1, §6.2.3, §23.4.2, §23.4.3.2 • [SutterOO] §40-41, §46 • [SutterO4] §29
14 C++ Coding Standards
7. Know when and how to code for scalability.
Summary
Beware of explosive data growth: Without optimizing prematurely, keep an eye on
asymptotic complexity. Algorithms that work on user data should take a predictable,
and preferably no worse than linear, time with the amount of data processed. When
optimization is provably necessary and important, and especially if it's because data
volumes are growing, focus on improving big-Oh complexity rather than on
micro-optimizations like saving that one extra addition.
Discussion
This Item illustrates one significant balance point between Items 8 and 9, "don't op-timize
prematurely" and "don't pessimize prematurely." That makes this a tough
Item to write, lest it be misconstrued as "premature optimization." It is not that.
Here's the background and motivation: Memory and disk capacity continue to grow
exponentially; for example, from 1988 to 2004 disk capacity grew by about 112% per
year (nearly 1,900-fold growth per decade), whereas even Moore's Law is just 59%
per year (100-fold per decade). One clear consequence is that whatever your code
does today it may be asked to do tomorrow against more data—much more data. A
bad (worse than linear) asymptotic behavior of an algorithm will sooner or later
bring the most powerful system to its knees: Just throw enough data at it.
Defending against that likely future means we want to avoid "designing in" what
will become performance pits in the face of larger files, larger databases, more
pixels, more windows, more processes, more bits sent over the wire. One of the big
success factors in future-proofing of the C++ standard library has been its perform-ance
complexity guarantees for the STL container operations and algorithms.
Here's the balance: It would clearly be wrong to optimize prematurely by using a
less clear algorithm in anticipation of large data volumes that may never materialize.
But it would equally clearly be wrong to pessimize prematurely by turning a blind
eye to algorithmic complexity, a.k.a. "big-Oh" complexity, namely the cost of the
computation as a function of the number of elements of data being worked on.
There are two parts to this advice. First, even before knowing whether data volumes
will be large enough to be an issue for a particular computation, by default avoid using
algorithms that work on user data (which could grow) but that don't scale well with
data unless there is a clear clarity and readability benefit to using a less scalable
algorithm (see Item 6). All too often we get surprised: We write ten pieces of code
thinking they'll never have to operate on huge data sets, and then we'll turn out to be
perfectly right nine of the ten times. The tenth time, we'll fall into a performance
Design Style 15
pit—we know it has happened to us, and we know it has happened or will happen to
you. Sure, we go fix it and ship the fix to the customer, but it would be better to avoid
such embarrassment and rework. So, all things being equal (including clarity and
readability), do the following up front:
• Use flexible, dynamically-allocated data and instead of fixed-size arrays: Arrays "larger
than the largest I'll ever need" are a terrible correctness and security fallacy. (See
Item 77.) Arrays are acceptable when sizes really are fixed at compile time.
• Know your algorithm's actual complexity: Beware subtle traps like linear-seeming
algorithms that actually call other linear operations, making the algorithm actu
ally quadratic. (See Item 81 for an example.)
• Prefer to use linear algorithms or faster wherever possible: Constant-time complexity,
such as push_back and hash table lookup, is perfect (see Items 76 and 80). O(log
N) logarithmic complexity, such as set/map operations and lower_bound and
upper_bound with random-access iterators, is good (see Items 76, 85, and 86).
O(N) linear complexity, such as vector::insert and for each, is acceptable (see
Items 76, 81, and 84).
• Try to avoid worse-than-linear algorithms where reasonable: For example, by default
spend some effort on finding a replacement if you're facing a O(N log N) or
O(N2) algorithm, so that your code won't fall into a disproportionately deep per
formance pit in the event that data volumes grow significantly. For example,
this is a major reason why Item 81 advises to prefer range member functions
(which are generally linear) over repeated calls of their single-element counter
parts (which easily becomes quadratic as one linear operation invokes another
linear operation; see Example 1 of Item 81).
• Never use an exponential algorithm unless your back is against the wall and you really
have no other option: Search hard for an alternative before settling for an exponen
tial algorithm, where even a modest increase in data volume means falling off a
performance cliff.
Second, after measurements show that optimization is necessary and important, and
especially if it's because data volumes are growing, focus on improving big-Oh
complexity rather than on micro-optimizations like saving that one extra addition.
In sum: Prefer to use linear (or better) algorithms wherever possible. Avoid polyno-mial
algorithms where reasonable. Avoid exponential algorithms with all your
might.
References
[Bentley00] §6, §8, Appendix 4 * [Cormen01] • [Kernighan99] §7 • [Knuth97a] • [Knuth97b] •
[Knuth98] • [McConnell93] §5.1-4, §10.6 • [Murray93] §9.11 • lSedgewick98] •
[Stroustrup00] §17.1.2
16 C++ Coding Standards
8. Don't optimize prematurely.
Summary
Spur not a willing horse (Latin proverb): Premature optimization is as addictive as it is
unproductive. The first rule of optimization is: Don't do it. The second rule of op-timization
(for experts only) is: Don't do it yet. Measure twice, optimize once.
Discussion
As [Stroustrup00] §6's introduction quotes so deliriously:
Premature optimization is the root of all evil. —Donald Knuth [quoting Hoare] On
the other hand, we cannot ignore efficiency. —Jon Bentley
Hoare and Knuth are, of course and as always, completely correct (see Item 6 and
this Item). So is Bentley (see Item 9).
We define premature optimization as making designs or code more complex, and so
less readable, in the name of performance when the effort is not justified by a proven
performance need (such as actual measurement and comparison against goals) and
thus by definition adds no proven value to your program. All too often, unneeded and
unmeasured optimization efforts don't even make the program any faster.
Always remember:
It is far, far easier to make a correct program fast
than it is to make a fast program correct.
So, by default, don't focus on making code fast; focus first on making code as clear
and readable as possible (see Item 6). Clear code is easier to write correctly, easier to
understand, easier to refactor—and easier to optimize. Complications, including op-timizations,
can always be introduced later—and only if necessary.
There are two major reasons why premature optimizations frequently don't even
make the program faster. First, we programmers are notoriously bad at estimating
what code will be faster or smaller, and where the bottlenecks in our code will be.
This includes the authors of this book, and it includes you. Consider: Modern com-puters
feature an extremely complex computational model, often with several pipe-lined
processing units working in parallel, a deep cache hierarchy, speculative exe-cution,
branch prediction... and that's just the CPU chip. On top of the hardware,
compilers take their best guess at transforming your source code into machine code
that exploits the hardware at its best. And on top of all that complication, it's... well, it's
your guess. So if you go with nothing but guesswork, there is little chance your
ill-targeted micro-optimizations will significantly improve things. So, optimization
must be preceded by measurement; and measurement must be preceded by optimi-
Design Style 17
zation goals. Until the need is proven, your focus should be on priority #1—writing
code for humans. (When someone asks you to optimize, do demand proof.)
Second, in modern programs, increasingly many operations aren't CPU-bound
anyway. They may be memory-bound, network-bound, disk-bound, waiting on a
web service, or waiting on a database. At best, tuning application code in such op-erations
only make the operations wait faster. It also means that the programmer
wasted valuable time improving what didn't need improving instead of adding
value by improving what did.
Of course, the day will come when you do need to optimize some code. When you
do so, look first for an algorithmic optimization (see Item 7) and try to encapsulate
and modularize the optimization (e.g., in a function or class; see Items 5 and 11), and
clearly state in a comment the reason of the optimization and a reference to the algo-rithm
used.
A common beginner's mistake is to write new code while obsessing—with pride!—
over optimal execution at the cost of understandability. More often than not, this
yields miles of spaghetti that, even if correct in the beginning, is hard to read and
change. (See Item 6.)
It is not premature optimization to pass by reference (see Item 25), to prefer calling
prefix ++ and -- (see Item 28), and use similar idioms that should just naturally flow
out of our fingertips. These are not premature optimizations; they are simply avoiding
premature pessimizations (see Item 9).
Examples
Example: An inline irony. Here is a simple demonstration of the hidden cost of a pre-mature
micro-optimization: Profilers are excellent at telling you, by function hit
count, what functions you should have marked inline but didn't; profilers are terrible
at telling you what functions you did mark inline but shouldn't have. Too many
programmers "inline by default" in the name of optimization, nearly always trading
higher coupling for at best dubious benefit. (This assumes that writing inline even
matters on your compiler. See [SutterOO], [Sutter02], and [Sutter04].)
Exceptions
When writing libraries, it's harder to predict what operations will end up being used in
performance-sensitive code. But even library authors run performance tests
against a broad range of client code before committing to obfuscating optimizations.
References
[Bentley00] §6 • [Cline99] §13.01-09 • [Kernighan99] §7 • [Lakos96] §9.1.14 • [Meyers97] §33
• [Murray93] §9.9-10, §9.13 • [StroustrupOO] §6 introduction • [Sutter00] §30, §46 • [Sutter02]
§12 • [Sutter04] §25
18 C++ Coding Standards
9. Don't pessimize prematurely.
Summary
Easy on yourself, easy on the code: All other things being equal, notably code com-plexity
and readability, certain efficient design patterns and coding idioms should
just flow naturally from your fingertips and are no harder to write than the
pes-simized alternatives. This is not premature optimization; it is avoiding gratuitous
pessimization.
Discussion
Avoiding premature optimization does not imply gratuitously hurting efficiency. By
premature pessimization we mean writing such gratuitous potential inefficiencies as:
• Defining pass-by-value parameters when pass-by-reference is appropriate. (See
Item25.)
• Using postfix + + when the prefix version is just as good. (See Item 28.)
• Using assignment inside constructors instead of the initializer list. (See Item 48.)
It is not a premature optimization to reduce spurious temporary copies of objects,
especially in inner loops, when doing so doesn't impact code complexity. Item 18
encourages variables that are declared as locally as possible, but includes the excep-tion
that it can be sometimes beneficial to hoist a variable out of a loop. Most of the
time that won't obfuscate the code's intent at all, and it can actually help clarify
what work is done inside the loop and what calculations are loop-invariant. And of
course, prefer to use algorithms instead of explicit loops. (See Item 84.)
Two important ways of crafting programs that are simultaneously clear and efficient
are to use abstractions (see Items 11 and 36) and libraries (see Item 84). For example,
using the standard library's vector, list, map, find, sort and other facilities, which
have been standardized and implemented by world-class experts, not only makes
your code clearer and easier to understand, but it often makes it faster to boot.
Avoiding premature pessimization becomes particularly important when you are
writing a library. You typically can't know all contexts in which your library will be
used, so you will want to strike a balance that leans more toward efficiency and re-usability
in mind, while at the same time not exaggerating efficiency for the benefit of
a small fraction of potential callers. Drawing the line is your task, but as Item 7 shows,
the bigger fish to focus on is scalability and not a little cycle-squeezing.
References
[Keffer95] pp.12-13 • [Stroustrup00] §6 introduction • [Sutter00] §6
Design Style 19
10. Minimize global and shared data.
Summary
Sharing causes contention: Avoid shared data, especially global data. Shared data in-creases
coupling, which reduces maintainability and often performance.
Discussion
This statement is more general than Item 18's specific treatment.
Avoid data with external linkage at namespace scope or as static class members.
These complicate program logic and cause tighter coupling between different (and,
worse, distant) parts of the program. Shared data weakens unit testing because the
correctness of a piece of code that uses shared data is conditioned on the history of
changes to the data, and further conditions the functioning of acres of yet-unknown
code that subsequently uses the data further.
Names of objects in the global namespace additionally pollute the global namespace.
If you must have global, namespace-scope, or static class objects, be sure to initialize
such objects carefully. The order of initialization of such objects in different compila-tion
units is undefined, and special techniques are needed to handle it correctly (see
References). The order-of-initialization rules are subtle; prefer to avoid them, but if
you do have to use them then know them well and use them with great care.
Objects that are at namespace scope, static members, or shared across threads or
processes will reduce parallelism in multithreaded and multiprocessor environ-ments
and are a frequent source of performance and scalability bottlenecks. (See
Item 7.) Strive for "shared-nothing;" prefer communication (e.g., message queues)
over data sharing.
Prefer low coupling and minimized interactions between classes. (See [Cargill92].)
Exceptions
The program-wide facilities cin, cout, and cerr are special and are implemented spe-cially.
A factory has to maintain a registry of what function to call to create a given
type, and there is typically one registry for the whole program (but preferably it
should be internal to the factory rather than a shared global object; see Item 11).
Code that does share objects across threads should always serialize all access to
those shared objects. (See Item 12 and [Sutter04c].)
References
[Cargill92] pp. 126.136, 169-173 • [DewhurstO3] §3 • [Lakos96] §2.3.1 • [McConnell93]
§5.1-4 • [Stroustrup00] §C.10.1 • [Sutter00] §47 • [SutterO2] §16, Appendix A •
[Sutter04c] • [SuttHysl03]
20 C++ Coding Standards
11. Hide information.
Summary
Don't tell: Don't expose internal information from an entity that provides an abstraction.
Discussion
To minimize dependencies between calling code that manipulates an abstraction
and the abstraction's implementation(s), data that is internal to the implementation
must be hidden. Otherwise, calling code can access—or, worse, manipulate—that in-formation,
and the intended-to-be-internal information has leaked into the abstrac-tion
on which calling code depends. Expose an abstraction (preferably a domain ab-straction
where available, but at least a get/set abstraction) instead of data.
Information hiding improves a project's cost, schedule, and/or risk in two main ways:
• It localizes changes: Information hiding reduces the "ripple effect" scope of
changes, and therefore their cost.
• It strengthens invariants: It limits the code responsible for maintaining (and, if it
is buggy, possibly breaking) program invariants. (See Item 41.)
Don't expose data from any entity that provides an abstraction (see also Item 10).
Data is just one possible incarnation of abstract, conceptual state. If you focus on
concepts and not on their representations you can offer a suggestive interface and
tweak implementation at will—such as caching vs. computing on-the-fly or using
various representations that optimize certain usage patterns (e.g., polar vs. Carte-sian).
A common example is to never expose data members of class types by making them
public (see Item41) or by giving out pointers or handles to them(see Item42), but this
applies equally to larger entities such as libraries, which must likewise not expose
internal information. Modules and libraries likewise prefer to provide interfaces that
define abstractions and traffic in those, and thereby allow communication with
calling code to be safer and less tightly coupled than is possible with data sharing.
Exceptions
Testing code often needs white-box access to the tested class or module.
Value aggregates ("C-style structs") that simply bundle data without providing any
abstraction do not need to hide their data; the data is the interface. (See Item 41.)
References
[Brooks95] §19* [McConnell93] §6.2 • [ParnasO2] • [StroustrupOO] §24.4 • [SuttHyslO4a]
Design Style 21
12. Know when and how to code for
concurrency.
Summary
Thsflreaygd/y. 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 tech-niques.
Among the most important issues are to avoid deadlocks, livelocks, and ma-lign
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:
• Consult your target platforms' documentation for local synchronization primitives:
Typical ones range from lightweight atomic integer operations to memory barri
ers to in-process and cross-process mutexes.
• 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 li
brary (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 par
ticular, each type must at minimum:
• Guarantee that unshared objects are independent: Two threads can freely use dif
ferent 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 ob
jects, but some types do not; the latter typically either design away the lock
ing 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 granular
ity 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
22 C++ Coding Standards
want to use standard library components (e.g., string, containers) in a multi-threaded
program, consult your standard library implementation's documenta-tion
to see whether that is supported, as described earlier.
When authoring your own type that is intended to be usable in a multithreaded pro-gram,
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 pub
lic member function, so that callers may not need to serialize uses of the object. For ex
ample, producer/consumer queues typically use internal locking, because their
whole raison d'etre is to be shared across threads, and their interfaces are de
signed 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 ap
propriate 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 opera-tions.
If the caller typically needs to lock several operations, rather than an op-eration,
this is inappropriate; individually locked functions can only be assem-bled
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 amember 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.emptyO ) c.push_back(x);. (See
[SutterO2] 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.
Design Style 23
So, internal locking is tied to the type's public interface: Internal locking be-comes
appropriate when the type's individual operations are complete in them-selves;
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 al-ternatives:
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 implemen-tation-
sharing. (See [SutterO4c].) 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 over-head
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
[AlexandrescuO2a] • [AlexandrescuO4] • [Butenhof97] • [HenneyOO] • [HenneyOl] •
[MeyersO4] • [SchmidtOl] • [StroustrupOO] §14.9 • [SutterO2] §16 • [SutterO4c]
24 C++ Coding Standards
13. Ensure resources are owned by objects.
Use explicit RAII and smart pointers.
Summary
Don't saw by hand when you have power tools: C++'s "resource acquisition is initiali-zation"
(RAII) idiom is the power tool for correct resource handling. RAII allows the
compiler to provide strong and automated guarantees that in other languages require
fragile hand-coded idioms. When allocating a raw resource, immediately pass it to an
owning object. Never allocate more than one resource in a single statement.
Discussion
C++'s language-enforced constructor/destructor symmetry mirrors the symmetry
inherent in resource acquire/release function pairs such as fopen/fclose,
lock/unlock, and new/delete. This makes a stack-based (or reference-counted) object
with a resource-acquiring constructor and a resource-releasing destructor an excellent
tool for automating resource management and cleanup.
The automation is easy to implement, elegant, low-cost, and inherently error-safe. If
you choose not to use it, you are choosing the nontrivial and attention-intensive task of
pairing the calls correctly by hand, including in the presence of branched control
flows and exceptions. Such C-style reliance on micromanaging resource deallocation
is unacceptable when C++ provides direct automation via easy-to-use RAII.
Whenever you deal with a resource that needs paired acquire/release function calls,
encapsulate that resource in an object that enforces pairing for you and performs the
resource release in its destructor. For example, instead of calling a pair of
Open-Port/ClosePort nonmember functions directly, consider:
class Port {
public:
Port( const strings destination ); // call OpenPort
~ Port(); // call ClosePort
//... ports can't usually be cloned, so disable copying and assignment...
};
void DoSomething() { Port
portl( "serverl:80" );
} //can't forget to close portl; it's closed automatically at the end of the scope
shared_ptr< Port> port2 =/*... */ //port2 is closed automatically when the
//last shared_ptr referring to it goes away
You can also use libraries that implement the pattern for you (see [AlexandrescuOOc]).
Design Style 25
When implementing RAII, be conscious of copy construction and assignment (see
Item 49); the compiler-generated versions probably won't be correct. If copying
doesn't make sense, explicitly disable both by making them private and not defined
(see Item 53). Otherwise, have the copy constructor duplicate the resource or refer-ence-
count the number of uses, and have the assignment operator do the same and
ensure that it frees its originally held resource if necessary. A classic oversight is to
free the old resource before the new resource is successfully duplicated (see Item 71).
Make sure that all resources are owned by objects. Prefer to hold dynamically allo-cated
resources via smart pointers instead of raw pointers. Also, perform every ex-plicit
resource allocation (e.g., new) in its own statement that immediately gives the
allocated resource to a manager object (e.g., sharedptr); otherwise, you can leak re-sources
because the order of evaluation of a function's parameters is undefined. (See
Item 31.) For example:
void Fun( shared_ptr<Widget> spl, shared_ptr<Widget> sp2 );
Fun( shared_ptr<Widget>(new Widget), shared_ptr<Widget>(new Widget) );
Such code is unsafe. The C++ Standard gives compilers great leeway to reorder the
two expressions building the function's two arguments. In particular, the compiler
can interleave execution of the two expressions: Memory allocation (by calling op-erator
new) could be done first for both objects, followed by attempts to call the two
Widget constructors. That very nicely sets things up for a leak because if one of the
constructor calls throws an exception, then the other object's memory will never be
released! (See [SutterO2] for details.)
This subtle problem has a simple solution: Follow the advice to never allocate more
than one resource in a single statement, and perform every explicit resource alloca-tion
(e.g., new) in its own code statement that immediately gives the resource to an
owning object (e.g., shared_ptr). For example:
shared_ptr spl(newWidget), sp2(newWidget);
Fun( spl, sp2 );
See also Item 31 for other advantages to using this style.
Exceptions
Smart pointers can be overused. Raw pointers are fine in code where the pointed-to
object is visible to only a restricted quantity of code (e.g., purely internal to a class,
such as a Tree class's internal node navigation pointers).
References
[AlexandrescuOOc] • [Cline99] §31.03-05 • [DewhurstO3] §24, §67 • [Meyers96] §9-10 •
[MilewskiOl] • [StroustrupOO] §14.3-4, §25.7, §E.3, §E.6 • [SutterOO] §16 • [SutterO2]
§20-21 • [Vandevoorde03] §20.1.4
Coding Style
One man's constant is another man's variable.
—Alan Perlis
In this section, we tighten our focus from general design issues to issues that arise
most often during actual coding.
The rules and guidelines in this section target coding practices that aren't specific to a
particular language area (e.g., functions, classes, or namespaces) but that improve the
quality of your code. Many of these idioms are about getting your compiler to help
you, including the powerful tool of declarative const (Item 15) and internal #include
guards (Item 24). Others will help you steer clear of land mines (including some
outright undefined behavior) that your compiler can't always check for you,
including avoiding macros (Item 16) and uninitialized variables (Item 19). All of
them help to make your code more reliable.
Our vote for the most valuable Item in this section goes to Item 14: Prefer
compile-and link-time errors to run-time errors.
27
28 C++ Coding Standards
14. Prefer compile- and link-time errors to
run-time errors.
Summary
Don't put off 'til run time what you can do at build time: Prefer to write code that uses
the compiler to check for invariants during compilation, instead of checking them at
run time. Run-time checks are control- and data-dependent, which means you'll
seldom know whether they are exhaustive. In contrast, compile-time checking is not
control- or data-dependent and typically offers higher degrees of confidence.
Discussion
The C++ language offers many opportunities to "accelerate" error detection by
pushing it to compilation time. Exploiting these static checking capabilities offers
you many advantages, including the following:
• Static checks are data- and flow-independent: Static checking offers guarantees that
are independent of the program inputs or execution flow. In contrast, to make
sure that your run-time checking is strong enough, you need to test it for a rep
resentative sample of all inputs. This is a daunting task for all but the most triv
ial systems.
• Statically expressed models are stronger: Oftentimes, a program that relies less on
run-time checks and more on compile-time checks reflects a better design be
cause the model the program creates is properly expressed using C++'s type
system. This way, you and the compiler are partners having a consistent view of
the program's invariants; run-time checks are often a fallback to do checking
that could be done statically but cannot be expressed precisely in the language.
(See Item68.)
• Static checks don't incur run-time overhead: With static checks replacing dynamic
checks, the resulting executable will be faster without sacrificing correctness.
One of C++'s most powerful static checking tools is its static type checking. The de-bate
on whether types should be checked statically (C++, Java, ML, Haskell) or dy-namically
(Smalltalk, Ruby, Python, Lisp) is open and lively. There is no clear winner in
the general case, and there are languages and development styles that favor either kind
of checking with reportedly good results. The static checking crowd argues that a large
category of run-time error handling can be thus easily eliminated, resulting in
stronger programs. On the other hand, the dynamic checking camp says that com-
Coding Style 29
pilers can only check a fraction of potential bugs, so if you need to write unit tests
anyway you might as well not bother with static checking at all and get a less re-strictive
programming environment.
One thing is clear: Within the context of the statically typed language C++, which
provides strong static checking and little automatic run-time checking, programmers
should definitely use the type system to their advantage wherever possible (see also
Items 90 through 100). At the same time, run-time checks are sensible for data- and
flow-dependent checking (e.g., array bounds checking or input data validation) (see
Items 70 and 71).
Examples
There are several instances in which you can replace run-time checks with
compile-time checks.
Example 1: Compile-time Boolean conditions. If you are testing for compile-time Boolean
conditions such as sizeof(int) > = 8, use static assertions instead of run-time tests.
(But see also Item 91.)
Example 2: Compile-time polymorphism. Consider replacing run-time polymorphism
(virtual functions) with compile-time polymorphism (templates) when defining ge-neric
functions or types. The latter yields code that is better checked statically. (See
also Item64.)
Example 3: Enums. Consider defining enums (or, better yet, full-fledged types) when
you need to express symbolic constants or restricted integral values.
Example 4: Downcasting. If you frequently use dynamic_cast (or, worse, an unchecked
static_cast) to perform downcasting, it can be a sign that your base classes offer too
little functionality. Consider redesigning your interfaces so that your program can
express computation in terms of the base class.
Exceptions
Some conditions cannot be checked at compile time and require run-time checks.
For these, prefer to use assertions to detect internal programming errors (see Item 68)
and follow the advice in the rest of the error handling section for other run-time errors
such as data-dependent errors (see Items 69 through 75).
References
[AlexandrescuOl] §3 • [Boost] • [Meyers97] §46 • [StroustrnpOO] §2.4.2 • [SutterO2] §4 •
[SutterO4] §2, §19
30 C++ Coding Standards
15. Use const proactively.
Summary
const is your friend: Immutable values are easier to understand, track, and reason
about, so prefer constants over variables wherever it is sensible and make const
your default choice when you define a value: It's safe, it's checked at compile time
(see Item 14), and it's integrated with C++'s type system. Don't cast away const ex-cept
to call a const-incorrect function (see Item 94).
Discussion
Constants simplify code because you only have to look at where the constant is de-fined
to know its value everywhere. Consider this code:
void Fun( vector<int>& v)
{ //...
const size_t len = v.size();
//... 30 more lines ... }
When seeing len's definition above, you gain instant confidence about len's seman-tics
throughout its scope (assuming the code doesn't cast away const, which it
should not do; see below): It's a snapshot of v's length at a specific point. Just by
looking up one line of code, you know len's semantics over its whole scope. Without
the const, len might be later modified, either directly or through an alias. Best of all, the
compiler will help you ensure that this truth remains true.
Note that const is not deep. For example, consider a class C that has a member of
type X*. In C objects that are const, the X* member is also const—but the X object that
is pointed to is not. (See [Saks99].)
Implement logical constness with mutable members.When a const member function
of a class legitimately needs to modify a member variable (i.e., when the variable
does not affect the object's observable state, such as cached data), declare that
member variable mutable. Note that if all private members are hidden using the
Pimpl idiom (see Item 43), mutable is not needed on either the cached information or
the unchanging pointer to it.
Yes, const is "viral"—add it in one place, and it wants to propagate throughout your
code as you call other functions whose signatures aren't yet const-correct. This is a
Coding Style 31
feature, not a bug, and this quality greatly increases const's power even though it
was unjustly demeaned in the days when const wasn't well understood and appre-ciated.
Retrofitting an existing code base to make it const-correct takes effort, but it is
worthwhile and likely to uncover latent bugs.
Const-correctness is worthwhile, proven, effective, and highly recommended. Un-derstanding
how and where a program's state changes is vital, and const documents
that directly in code where the compiler can help to enforce it. Writing const appro-priately
helps you gain a better understanding of your design and makes your code
sturdier and safer. If you find it impossible to make a member function const, you
usually gain a better understanding of the ways in which that member function
might modify an object's state. You might also understand which data members
bridge the gap between physical constness and logical constness, as noted in the fol-lowing
Examples.
Never cast away const except to call a const-incorrect function, or in rare cases as a
workaround for lack of mutable on older compilers.
Examples
Example: Avoid const pass-by-value function parameters in function declarations. The fol-lowing
two declarations are exactly equivalent:
void Fun( int x );
void Fun( const int x ); //redeclares the same function: top-level const is ignored
In the second declaration, the const is redundant. We recommend declaring func-tions
without such top-level consts, so that readers of your header files won't get
confused. However, the top-level const does make a difference in a function's defini-tion
and can be sensible there to catch unintended changes to the parameter:
void Fun( const int x ) { //Fun's actual definition
++x; // error: cannot modify a const value
}
References
[Allison98] §10 • [Cline99] §14.02-12 • [DewhurstO3] §6, §31-32, §82 • [Keffer95] pp. 5-6 •
[Koenig97] §4 • [Lakos96] §9.1.6, §9.1.12 • [Meyers97] §21 • [Murray93] §2.7 •
[StroustrupOO] §7.2, §10.2.6, §16.3.1 • [SutterOO] §43
32 C++ Coding Standards
16. Avoid macros.
Summary
TO_PUT_IT_BLUNTLY: Macros are the bluntest instrument of C and C++'s abstraction
facilities, ravenous wolves in functions' clothing, hard to tame, marching to their
own beat all over your scopes. Avoid them.
Discussion
It's hard to find language that's colorful enough to describe macros, but we'll try. To
quote from[SutterO4] §31:
Macros are obnoxious, smelly, sheet-hogging bedfellows for several reasons, most
of which are related to the fact that they are a glorified text-substitution facility
whose effects are applied during preprocessing, before any C++ syntax and semantic
rules can even begin to apply.
Lest there remain any ambiguity on this point, we note also that Bjarne Stroustrup
has written:
I dislike most forms of preprocessors and macros. One ofC++'s aims is to make C's
preprocessor redundant (§4.4, §18) because I consider its actions inherently error
prone. —[Stroustrup94] §3.3.1
Macros are almost never necessary in C++. Use const (§5.4) or enum (§4.8) to
define manifest constants [see Item 15], inline (§7.1.1) to avoid function-calling
overhead [but see Item 8], templates (Chapter 13) to specify families of functions
and types [see Items 64 through 67], and namespaces (§8.2) to avoid name
clashes [see Items 57 through 59].—[StroustrupOO] §1.6.1
The first rule about macros is: Don't use them unless you have to. Almost every
macro demonstrates a flaw in the programming language, in the program, or in
the programmer. —[StroustrupOO] §7.8
The main problem with C++ macros is that they seem much better at what they do
than they really are. Macros ignore scopes, ignore the type system, ignore all other
language features and rules, and hijack the symbols they #define for the remainder
of a file. Macro invocations look like symbols or function calls, but are neither. Macros
are not "hygienic," meaning that they can expand to significantly and surprisingly
different things depending on the context in which they are used. The text
substitution that macros perform makes writing even remotely proper macros a
black art whose mastery is as unrewarding as it is tedious.
Coding Style 33
People who think that template-related errors are the worst to decipher probably
haven't seen those caused by badly formed or badly used macros. Templates are
part of C++'s type system and thus allow compilers to get better at handling them
(which they do), whereas macros are forever divorced from the language and hence
intractable. Worse, unlike a template, a macro might expand to some transmission
line noise that undesirably compiles by pure chance. Finally, an error in a macro can
only be reported after the macro is expanded and not when it is defined.
Even in the rare cases where you do legitimately write a macro (see Exceptions),
never ever even consider starting to think about writing a macro that is a common
word or abbreviation. Do #undefine macros as soon as possible, always give them
SCREAMING_UPPERCASE_AND_UGLY names, and avoid putting themin headers.
Examples
Example: Passing a template instantiation to a macro. Macros barely understand C's pa-rentheses
and square brackets well enough to balance them. C++, however, defines a
new parenthetical construct, namely the < and > used in templates. Macros can't
pair those correctly, which means that in a macro invocation
MACRO( Focxint, double> )
the macro thinks it is being passed two arguments, namely Focxint and double>,
when in fact the construct is one C++ entity.
Exceptions
Macros remain the only solution for a few important tasks, such as #include guards
(see Item 24), #ifdef and #if defined for conditional compilation, and implementing
assert (see Item 68).
For conditional compilation (e.g., system-dependent parts), avoid littering your code
with #ifdefs. Instead, prefer to organize code such that the use of macros drives al-ternative
implementations of one common interface, and then use the interface
throughout.
You may want to use macros (cautiously) when the alternative is extreme copying
and pasting snippets of code around.
We note that both [C99] and [Boost] include moderate and radical extensions, re-spectively,
to the preprocessor.
References
[Boost] • [C99] • [DewhurstO3] §25-28 • [Lakos96] §2.3.4 «
[StroustrupOO] §1.6.1, §7.8 • [SutterO2] §34-35 • [SutterO4] §31
[Stroustrup94]
[SutterO4a]
53.3.2
34 C++ Coding Standards
17. Avoid magic numbers.
Summary
Programming isn't magic, so don't incant it: Avoid spelling literal constants like 42 or
3.14159 in code. They are not self-explanatory and complicate maintenance by
adding a hard-to-detect form of duplication. Use symbolic names and expressions
instead, such as width * aspectRatio.
Discussion
Names add information and introduce a single point of maintenance; raw numbers
duplicated throughout a program are anonymous and a maintenance hassle. Con-stants
should be enumerators or const values, scoped and named appropriately.
One 42 may not be the same as another 42. Worse, "in-head" computations made by
the programmer (e.g., "this 84 comes from doubling the 42 used five lines ago")
make it tedious and error-prone to later replace 42 with another constant.
Prefer replacing hardcoded strings with symbolic constants. Keeping strings sepa-rate
from the code (e.g., in a dedicated .cpp or resource file) lets non-programmers
review and update them, reduces duplication, and helps internationalization.
Examples
Example 1: Important domain-specific constants at namespace level,
const sizej PAGE_SIZE = 8192,
WORDS_PER_PAGE = PAGE_SIZE/sizeof(int),
INFO_BITS_PER_PAGE = 32 * CHAR_BIT;
Example 2: Class-specific constants. You can define static integral constants in the class
definition; constants of other types need a separate definition or a short function.
//File widget.h
classWidget {
static const int defaultWidth = 400; // value provided in declaration
static const double defaultPercent; // value provided in definition
static const char* Name() {return "Widget";}
};
//File widget.cpp
const double Widget::defaultPercent = 66.67; // value provided in definition
const int Widget::defaultWidth; //definition required
References
[DewhurstO3] §2 • [Kernighan99] §1.5 • [StroustrupOO] §4.8, §5.4
Coding Style 35
18. Declare variables as locally as possible.
Summary
Avoid scope bloat, as with requirements so too with variables): Variables introduce
state, and you should have to deal with as little state as possible, with lifetimes as
short as possible. This is a specific case of Item 10 that deserves its own treatment.
Discussion
Variables whose lifetimes are longer than necessary have several drawbacks:
• They make the program harder to understand and maintain: For example, should
code update the module-wide path string if it only changes the current drive?
• They pollute their context with their name: As a direct consequence of this, name-space-
level variables, which are the most visible of all, are also the worst (see
Item 10).
• They can't always be sensibly initialized: Never declare a variable before you can
initialize it sensibly. Uninitialized variables are a pervasive source of bugs in all
C and C++ programs, and they require our proactive attention because they
can't always be detected by compilers (see Item 19).
In particular, older versions of C before [C99] required variables to be defined only at
the beginning of a scope; this style is obsolete in C++. A serious problem with this
restriction is that at the beginning of the scope you often don't yet have enough in-formation
to initialize variables with pertinent information. This leaves you with
two choices—either initialize with some default blank value (e.g., zero), which is
usually wasteful and can lead to errors if the variable ends up being used before it has
a useful state, or leave them uninitialized, which is dangerous. An uninitialized
variable of user-defined types will self-initialize to some blank value.
The cure is simple: Define each variable as locally as you can, which is usually ex-actly
the point where you also have enough data to initialize it and immediately be-fore
its first use.
Exceptions
It can sometimes be beneficial to hoist a variable out of a loop. (See Item 9.)
Because constants don't add state, this Item does not apply to them. (See Item 17.)
References
[DewhurstO3] §3, §48, §66
[StroustrupOO] §4.9.4, §6.3
[Dewhurst03] §95 [McConnell93] §5.1-4, §10.1
36 C++ Coding Standards
19. Always initialize variables.
Summary
Start with a clean slate: Uninitialized variables are a common source of bugs in C
and C++ programs. Avoid such bugs by being disciplined about cleaning memory
before you use it; initialize variables upon definition.
Discussion
In the low-level efficiency tradition of C and C++ alike, the compiler is often not re-quired
to initialize variables unless you do it explicitly (e.g., local variables, forgotten
members omitted from constructor initializer lists). Do it explicitly.
There are few reasons to ever leave a variable uninitialized. None is serious enough to
justify the hazard of undefined behavior.
If you've used a procedural language (e.g., Pascal, C, Fortran, or Cobol) you might
be used to defining variables in separation from the code that uses them, and then
assigning them values later when they're about to be used. This approach is obsolete
and not recommended (see Item 18).
A common misconception about uninitialized variables is that they will crash the
program, so that those few uninitialized variables lying around here and there will be
quickly revealed by simple testing. On the contrary, programs with uninitialized
variables can run flawlessly for years if the bits in the memory happen to match the
program's needs. Later, a call from a different context, a recompilation, or some
change in another part of the program will cause failures ranging from inexplicable
behavior to intermittent crashes.
Examples
Example 1: Using a default initial value or ?: to reduce mixing dataflow with control flow.
//Not recommended: Doesn't initialize variable
int speedupFactor;
if( condition)
speedupFactor = 2;
else
speedupFactor = -1;
//Better: Initializes variable
int speedupFactor = -1;
if( condition ) speedupFactor
= 2;
Coding Style 37
// Better: Initializes variable
int speedupFactor = condition ? 2 : -1;
The better alternatives nicely leave no gap between definition and initialization.
Example 2: Replacing a complicated computational flow with a function. Sometimes a value
is computed in a way that is best encapsulated in a function (see Item 11):
//Not recommended: Doesn't initialize variable
int speedupFactor;
if( condition ) {
//... code...
speedupFactor = someValue; }
else {
//... code...
speedupFactor = someOtherValue; }
// Better: Initializes variable
int speedupFactor = ComputeSpeedupFactor();
Example 3: Initializing arrays. For large aggregate types such as arrays, proper initiali-zation
does not always mean having to really touch all the data. For example, say
you use an API that forces you to use fixed arrays of char of size MAX_PATH (but see
Items 77 and 78). If you are sure the arrays are always treated as null-terminated C
strings, this immediate assignment is good enough:
//Acceptable: Create an empty path
char path[MAX_PATH]; path[0] = \0';
The following safer initialization fills all the characters in the array with zero:
//Better: Create a zero-filled path
char path[MAX_PATH] = {'\0' };
Both variants above are recommended, but in general you should prefer safety to
unneeded efficiency.
Exceptions
Input buffers and volatile data that is directly written by hardware or other proc-esses
does not need to be initialized by the program.
References
[DewhurstO3] §48 • [StroustrupOO] §4.9.5, §6.3
38 C++ Coding Standards
20. Avoid long functions. Avoid deep nesting.
Summary
Short is better than long, flat is better than deep: Excessively long functions and
nested code blocks are often caused by failing to give one function one cohesive re-sponsibility
(see Item 5), and both are usually solved by better refactoring.
Discussion
Every function should be a coherent unit of work bearing a suggestive name (see
Item 5 and the Discussion in Item 70). When a function instead tries to merge such
small conceptual elements inside a long function body, it ends up doing too much.
Excessive straight-line function length and excessive block nesting depth (e.g., if,
for, while, and try blocks) are twin culprits that make functions more difficult to un-derstand
and maintain, and often needlessly so.
Each level of nesting adds intellectual overhead when reading code because you
need to maintain a mental stack (e.g., enter conditional, enter loop, enter try, enter
conditional, ...). Have you ever found a closing brace in someone's code and won-dered
which of the many fors, whiles, or ifs it matched? Prefer better functional de-composition
to help avoid forcing readers to keep as much context in mind at a time.
Exercise common sense and reasonableness: Limit the length and depth of your
functions. All of the following good advice also helps reduce length and nesting:
• Prefer cohesion: Give one function one responsibility (see Item5).
• Don't repeat yourself: Prefer a named function over repeated similar code snippets.
• Prefer &&: Avoid nested consecutive ifs where an && condition will do.
• Don't try too hard: Prefer automatic cleanup via destructors over try blocks (see
Item13).
• Prefer algorithms: They're flatter than loops, and often better (see Item 84).
• Don't switch on type tags. Prefer polymorphic functions (see Item 90).
Exceptions
A function might be legitimately long and/or deep when its functionality can't be
reasonably refactored into independent subtasks because every potential refactoring
would require passing many local variables and context (rendering the result less
readable rather than more readable). But if several such potential functions take
similar arguments, they might be candidates for becoming members of a new class.
References
[Piwowarski82] • [Miller56]
Coding Style 39
21. Avoid initialization dependencies across
compilation units.
Summary
Keep (initialization) order: Namespace-level objects in different compilation units
should never depend on each other for initialization, because their initialization order
is undefined. Doing otherwise causes headaches ranging from mysterious crashes
when you make small changes in your project to severe non-portability even to new
releases of the same compiler.
Discussion
When you define two namespace-level objects in different compilation units, which
object's constructor is called first is not defined. Often (but not always) your tools
might happen to initialize them in the order in which the compilation units' object
files are linked, but this assumption is usually not reliable; even when it does hold,
you don't want the correctness of your code to subtly depend on your makefile or
project file. (For more on the evils of order dependencies, see also Item 59.)
Therefore, inside the initialization code of any namespace-level object, you can't as-sume
that any other object defined in a different compilation unit has already been
initialized. These considerations apply to dynamically initialized variables of primi-tive
types, such as a namespace-level bool reg_success = LibRegister("mylib");
Note that, even before they are ever constructed using a constructor,
namespace-level objects are statically initialized with all zeroes (as opposed to, say,
automatic objects that initially contain garbage). Paradoxically, this zero-initialization
can make bugs harder to detect, because instead of crashing your program swiftly the
static zero-initialization gives your yet-uninitialized object an appearance of
legitimacy. You'd think that that string is empty, that pointer is null, and that integer
is zero, when in fact no code of yours has bothered to initialize them yet.
To avoid this problem, avoid namespace-level variables wherever possible; they are
dangerous (see Item 10). When you do need such a variable that might depend upon
another, consider the Singleton design pattern; used carefully, it might avoid implicit
dependencies by ensuring that an object is initialized upon first access. Still, Singleton
is a global variable in sheep's clothing (see again Item 10), and is broken by mutual or
cyclic dependencies (again, zero-initialization only adds to the confusion).
References
[DewhurstO3] §55 • [Gamma95] • [McConnell93] §5.1-4 • [StronstrupOO] §9.4.1, §10.4.9
40 C++ Coding Standards
22. Minimize definitional dependencies.
Avoid cyclic dependencies.
Summary
Don't be over-dependent: Don't #include a definition when a forward declaration
will do.
Don't be co-dependent: Cyclic dependencies occur when two modules depend di-rectly
or indirectly on one another. A module is a cohesive unit of release (see page
103); modules that are interdependent are not really individual modules, but
super-glued together into what's really a larger module, a larger unit of release. Thus,
cyclic dependencies work against modularity and are a bane of large projects. Avoid
them.
Discussion
Prefer forward declarations except where you really need a type's definition. You
need a full definition of a class C in two main cases:
• When you need to know the size of a C object: For example, when allocating a C on
the stack or as a directly-held member of another type.
• When you need to name or call a member of C: For example, when calling a member
function.
In keeping with this book's charter, we'll set aside from the start those cyclic de-pendencies
that cause compile-time errors; you've already fixed them by following
good advice present in the literature and Item 1. Let's focus on cyclic dependencies
that remain in compilable code, see how they trouble your code's quality, and what
steps need be taken to avoid them.
In general, dependencies and their cycles should be thought of at module level. A
module is a cohesive collection of classes and functions released together (see Item 5
and page 103). In its simplest form, a cyclic dependency has two classes that directly
depend upon each other:
class Child; //breaks the dependency cycle
class Parent {//...
Child* myChild_; };
class Child {//... //possibly in a different header
Parent*
myParent_; };
Coding Style 41
Parent and Child depend upon each other. The code compiles, but we've set the
stage for a fundamental problem: The two classes are not independent anymore, but
have become interdependent. That is not necessarily bad, but it should only occur
when both are part of the same module (developed by the same person or team and
tested and released as a whole).
In contrast, consider: What if Child did not need to store a back link to its Parent
ob-ject? Then Child could be released as its own separate, smaller module (and maybe
under a different name) in total independence from Parent—clearly a more flexible
design.
Things get only worse when dependency cycles spanmultiple modules, which are all
stuck together with dependency glue to form a single monolithic unit of release.
That's why cycles are the fiercest enemy of modularity.
To break cycles, apply the Dependency Inversion Principle documented in
[Mar-tin96a] and [Martin00] (see also Item 36): Don't make high-level modules depend
on low-level modules; instead, make both depend on abstractions. If you can define in-dependent
abstract classes for either Parent or Child, you've broken the cycle. Oth-erwise,
you must commit to making them parts of the same module.
A particular form of dependency that certain designs suffer from is transitive de-pendency
on derived classes, which occurs when a base class depends on all of its
descendants, direct and indirect. Some implementations of the Visitor design pattern
leads to this kind of dependency. Such a dependency is acceptable only for excep-tionally
stable hierarchies. Otherwise, you may want to change your design; for ex-ample,
use the Acyclic Visitor pattern [Martin98].
One symptom of excessive interdependencies is incremental builds that have to
build large parts of the project in response to local changes. (See Item 2.)
Exceptions
Cycles among classes are not necessarily bad—as long as the classes are considered
part of the same module, tested together, and released together. Naive implementa-tions
of such patterns as Command and Visitor result in interfaces that are naturally
interdependent. These interdependencies can be broken, but doing so requires ex-plicit
design.
References
[AlexandrescuOl] §3 • [Boost] • [Gamma95] • [Lakos96] §0.2.1, §4.6-14, §5 • [Martin96a] •
[Martin96b] • [Martin98] §7 • [MartinOO] • [McConnell93] §5 • [Meyers97] §46 •
[StroustrupOO] §24.3.5 • [SutterOO] §26 • [SutterO2] §37 • [SutterO3]
42 C++ Coding Standards
23. Make header files self-sufficient.
Summary
Behave responsibly: Ensure that each header you write is compilable standalone, by
having it include any headers its contents depend upon.
Discussion
If one header file won't work unless the file that includes it also includes another
header, that's gauche and puts unnecessary burden on that header file's users.
Years ago, some experts advised that headers should not include other headers be-cause
of the cost of opening and parsing a guarded header multiple times. Fortu-nately,
this is largely obsolete: Many modern C++ compilers recognize header
guards automatically (see Item 24) and don't even open the same header twice.
Some also offer precompiled headers, which help to ensure that often-used,
seldom-changed headers will not be parsed often.
But don't include headers that you don't need; they just create stray dependencies.
Consider this technique to help enforce header self-sufficiency: In your build, compile
each header in isolation and validate that there are no errors or warnings.
Examples
Some subtler issues arise in connection with templates.
Example 1: Dependent names. Templates are compiled at the point where they are de-fined,
except that any dependent names or types are not compiled until the point
where the template is instantiated. This means that a template<class T> classWidget
with a std::deque<T> member does not incur a compile-time error even when
<deque> is not included, as long as nobody instantiates Widget. Given that Widget
exists in order to be instantiated, its header clearly should #include <deque>.
Example 2: Member function templates, and member functions of templates, are instantiated
only if used. Suppose that Widget doesn't have a member of type std::deque<T>, but
Widget's Transmogrify member function uses a deque. Then Widget's callers can
instantiate and use Widget just fine even if no one includes <deque>, as long as
they don't use Transmogrify. By default, the Widget header should still #include
<deque> because it is necessary for at least some callers of Widget. In rare cases
where an expensive header is being included for few rarely used functions of a tem-plate,
consider refactoring those functions as nonmembers supplied in a separate
header that does include the expensive one. (See Item 44.)
References
[Eakos96] §3.2 • [StroustrupOO] §9.2.3 • [SutterOO] §26-30 • [Vandevoorde03] §9-10
Coding Style 43
24. Always write internal #include guards.
Never write external #include guards.
Summary
Wear head(er) protection: Prevent unintended multiple inclusions by using #include
guards with unique names for all of your header files.
Discussion
Each header file should be guarded by an internal #include guard to avoid redefini-tions
in case it is included multiple times. For example, a header file foo.h should
follow the general form:
#ifndef FOO_H_INCLUDED_
#define FOO_HJNCLUDED_ //...
contents of the file... #endif
Observe the following rules when defining include guards:
• Use a unique guard name: Make sure it is unique at least within your application.
We used a popular convention above; the guard name can include the applica
tion name, and some tools generate guard names containing random numbers.
• Don't try to be clever: Don't put any code or comments before and after the
guarded portion, and stick to the standard form as shown. Today's preproces
sors can detect include guards, but they might have limited intelligence and ex
pect the guard code to appear exactly at the beginning and end of the header.
Avoid using the obsolete external include guards advocated in older books:
#ifndef FOO_HJNCLUDED_ //NOT recommended
#include "foo.h"
#define FOO_HJNCLUDED_
#endif
External include guards are tedious, are obsolete on today's compilers, and are fragile
with tight coupling because the callers and header must agree on the guard name.
Exceptions
In very rare cases, a header file may be intended to be included multiple times.
References
[C++03, §2.1] • [StroustrupOO] §9.3.3
Functions and Operators
If you have a procedure with ten parameters, you probably missed some.
—Alan Perlis
Functions, including overloaded operators, are the fundamental units of work. As
we will see later on in the section on Error Handling and Exceptions (and particularly
in Item 70), this has a direct effect on how we reason about the correctness and safety
of our code.
But first, let's consider some fundamental mechanics for writing functions, including
operators. In particular, we'll focus on their parameters, their semantics, and their
overloading.
Our vote for the most valuable Item in this section goes to Item 26: Preserve natural
semantics for overloaded operators.
45
46 C++ Coding Standards
25. Take parameters appropriately by value,
(smart) pointer, or reference.
Summary
Parameterize well: Distinguish among input, output, and input/output parameters,
and between value and reference parameters. Take them appropriately.
Discussion
Choosing well among values, references, and pointers for parameters is good habit
that maximizes both safety and efficiency.
Although efficiency should not be our primary up-front concern (see Item 8), neither
should we write needlessly inefficient code when all other things, including clarity,
are equal (see Item 9).
Prefer to follow these guidelines for choosing how to take parameters. For
input-only parameters:
• Always const-qualify all pointers or references to input-only parameters.
• Prefer taking inputs of primitive types (e.g., char, float) and value objects that
are cheap to copy (e.g., Point, complex<float>) by value.
• Prefer taking inputs of other user-defined types by reference to const.
• Consider pass-by-value instead of reference if the function requires a copy of its
argument. This is conceptually identical to taking a reference to const plus do
ing a copy, and it can help compiler to better optimize away temporaries.
For output or input/output parameters:
• Prefer passing by (smart) pointer if the argument is optional (so callers can pass
null as a "not available" or "don't care" value) or if the function stores a copy of
the pointer or otherwise manipulates ownership of the argument.
• Prefer passing by reference if the argument is required and the function won't
store a pointer to it or otherwise affect its ownership. This states that the argu
ment is required and makes the caller responsible for providing a valid object.
Don't use C-style varargs (see Item 98).
References
[AlexandrescuO3a] • [Cline99] §2.10-11, 14.02-12, 32.08 • [DeivhurstO3] §57 • [Koenig97]
§4 • [Lakos96] §9.1.11-12 • [McConnell93] §5.7 • [Meyers97] §21-22 • [Stroustrup94]
§11.4.4 • [StwustrupOO] §5.5, §11.6, §16.3.4 • [SutterOO] §6, §46
Functions and Operators 47
26. Preserve natural semantics for
overloaded operators.
Summary
Programmers hate surprises: Overload operators only for good reason, and preserve
natural semantics; if that's difficult, you might be misusing operator overloading.
Discussion
Although anyone would agree (we hope) that one should not implement subtraction
in an operator+ implementation, other cases can be subtle. For example, does your
Tensor class's operator* mean the scalar product or the vector product? Does
opera-tor+ =( Tensor& t, unsigned u ) add u to each of t's elements, or will it resize
t? In such ambiguous or counterintuitive cases, prefer using named functions instead
of fostering cryptic code.
For value types (but not all types; see Item 32): "When in doubt, do as the ints do."
[Meyers96]Mimicking the behavior of and relationships among operators on built-in
types ensures that you don't surprise anyone. If your semantics of choice are likely
to raise eyebrows, maybe operator overloading is not a good idea.
Programmers expect operators to come in bundles. If the expression a @ b is well
formed for some operator @ you define (possibly after conversions), ask: Can the
caller also write b@ a without surprises? Can the caller write a @= b? (See Item27.) If
the operator has an inverse (e.g., + and -, or * and /), are both supported?
Named functions are less likely to have such assumed relationships, and therefore
should be preferred for clearer code if there can be any doubt about semantics.
Exceptions
There are highly specialized libraries (e.g., parser generators and regular expression
engines) that define domain-specific conventions for operators that are very different
from their C++ meanings (e.g., a regular expression engine might use operator* to
express "zero or more"). Prefer instead to find an alternative to unusual operator
overloading (e.g., [C++TR104] regular expressions use strings, so that * can be used
naturally without overloading operators). If after careful thought you choose to use
operators anyway, make sure you define a coherent framework for your conventions
and that you don't step on the toes of any built-in operator.
References
[Cline99] §23.02-06 • [C++TR104] §7 • [DewhurstO3] §85-86 • [Koenig97] §4 • [Lakos96]
§9.1.1 • [Meyers96] §6 • [StroustrupOO] §11.1 • [SutterOOl §41
48 C++ Coding Standards
27. Prefer the canonical forms of arithmetic and
assignment operators.
Summary
If you a+b, also a+ = b: When defining binary arithmetic operators, provide their
as-signment versions as well, and write to minimize duplication and maximize
efficiency.
Discussion
In general, for some binary operator @ (be it +, -, *, and so on), you should define its
assignment version such that a @ = b and a = a @ b have the same meaning (other
than that the first form might be more efficient and only evaluates a once). The ca-nonical
way of achieving this goal is to define @ in terms of @=, as follows:
T& T::operator@= ( const T& ) {
//... implementation...
return *this; }
T operator@( const T& Ihs, const T& rhs) {
T temp( Ihs);
return temp @= rhs; }
The two functions work in tandem. The assignment form does the actual work and
returns its left-hand parameter. The non-assignment version creates a temporary
from Ihs, modifies it by invoking the assignment form, and returns it.
Note that here operator® is a nonmember function, so that it will have the desirable
property of accepting the same implicit conversions on its left-hand side and
right-hand side parameters. (See Item 44.) For example, if you define a class String
that has an implicit constructor taking a char, making operator+( const String&,
const String& ) a nonmember enables both char + String and String + char to work; a
member version String::operator+( const String& ) would only accept the latter. An
efficiency-minded implementation might choose to define several nonmember over-loads
of operator® to avoid proliferation of temporaries resulted through conver-sions
(see Item 29).
Where possible, make operator@ = a nonmember function as well (see Item 44). In
any case, put all nonmember operators in the same namespace as T so that they will
be conveniently available to callers as well as to avoid name lookup surprises (see
Item57).
Functions and Operators 49
A variation is to have operator@ accept its first parameter by value. This way, you
arrange for the compiler itself to perform the copy for you implicitly, and this can
give the compiler more leeway in applying optimizations:
T& operator@=( T& Ihs, const T& rhs ) {
//... implementation...
return Ihs; }
T operator@( T Ihs, const T& rhs) { //Ihs taken by value
return Ihs @= rhs; }
Another variation is to have operator@ return a const value. This technique has the
advantage that it disables nonsensical code such as a + b = c, but it does so at the
cost of disabling some potentially useful constructs such as a = (b + c).replace(pos, n,
d)—expressive code that, in one shot, concatenates strings b and c, replaces some
characters, and assigns the final result to a.
Examples
Example: An implementation of + = for strings. When concatenating strings, it is useful to
know the length in advance so as to allocate memory only once.
String& String::operator+=( const String& rhs)
{ //... implementation...
return *this; }
String operator+( const String& Ihs, const String& rhs) {
String temp; //initially empty
temp.Reserve( lhs.size() + rhs.size()); //allocate enough memory
return (temp += Ihs) += rhs; //append the strings and return
}
Exceptions
In some cases (e.g., operator* = on complex numbers), an operator might mutate its
left-hand side so significantly that it can be more advantageous to implement
opera-tor* = in terms of operator* rather than the reverse.
References
[AlexandrescuO3a] • [Cline99] §23.06 • [Meyers96] §22 • [SutterOO] §20
50 C++ Coding Standards
28. Prefer the canonical form of ++ and --.
Prefer calling the prefix forms.
Summary
If you + +c, also C+ + : The increment and decrement operators are tricky because
each has pre- and postfix forms, with slightly different semantics. Define opera-tor++
and operator-- such that they mimic the behavior of their built-in counterparts.
Prefer to call the prefix versions if you don't need the original value.
Discussion
An ancient joke about C++ was that the language is called C++ and not ++C because
the language is improved (incremented), but many people still use it as C (the previous
value). Fortunately, the joke is now obsolete, but it's a helpful illustration for
understanding the difference between the two operator forms.
For ++ and --, the postfix forms return the original value, whereas the prefix forms
return the new value. Prefer to implement the postfix form in terms of the prefix
form. The canonical form is:
T& T::operator+ + () { T& T::operator--() { // the prefix form:
//perform increment //perform decrement // - do the work
return *this; return *this; // - always return *this;
} }
T T::operator++(int) { T T::operator—(int) { //the postfix form:
Told(*this); T old( *this); // - remember old value
++*this; --*this; // - call the prefix version
return old; return old; // - return the old value
} }
In calling code, prefer using the prefix form unless you actually need the original
value returned by the postfix version. The prefix form is semantically equivalent,
just as much typing, and often slightly more efficient by creating one less object. This is
not premature optimization; it is avoiding premature pessimization (see Item 9).
Exceptions
Expression template frameworks preserve the semantics via different means.
References
[Cline99] §23.07-08 • [DewhurstO3] §87 • [Meyers96] §6 • [StroustrupOO] §19.3 •
[SutterOO] §6, §20
Functions and Operators 51
29. Consider overloading to avoid implicit type
conversions.
Summary
Do not multiply objects beyond necessity (Occam's Razor): Implicit type conversions
provide syntactic convenience (but see Item 40). But when the work of creating tempo-rary
objects is unnecessary and optimization is appropriate (see Item 8), you can pro-vide
overloaded functions with signatures that match common argument types ex-actly
and won't cause conversions.
Discussion
If you're in the office and run out of paper, what do you do? Of course, you walk to
your trusty photocopier and make several copies of a white sheet of paper.
As silly as it sounds, this is often what implicit conversions do: unnecessarily go
through the trouble of creating temporaries, just to perform some trivial operation
on them and toss them away (see Item 40). A common example is string comparison:
class String {//...
String( const char* text); // enables implicit conversion
};
bool operator= = ( const String&, const String& );
//... somewhere in the code...
if( someStr ing == "Hello") { . . . }
Having seen the definitions above, the compiler will compile the comparison as if
you had written s == String("Hello"). This can be quite wasteful, considering that
you don't need to copy the characters just to read them. The solution to this problem is
simple: define overloads that avoid the conversion. For example:
bool operator==( const String& Ihs, const String& rhs); / /#1
bool operator==( const String& Ihs, const char* rhs); // #2
bool operator==( const char* Ihs, const String& rhs); / /#3
That looks like a lot of code duplication, but in reality it is only "signature duplication"
because all three typically use the same back-end function. You're unlikely to commit a
premature optimization heresy (see Item 8) with such simple overloads, and it's de bon
gout to provide them especially when designing a library when it's difficult to predict in
advance what common types will be in performance-sensitive code.
References
[Meyers96] §21 • [StroustrupOO] §11.4, §C6 • [SutterOO] §6
52 C++ Coding Standards
30. Avoid overloading &&, ||, or, (comma).
Summary
Wisdom means knowing when to refrain: The built-in &&, ||, and , (comma) enjoy
special treatment from the compiler. If you overload them, they become ordinary func-tions
with very different semantics (you will violate Items 26 and 31), and this is a sure
way to introduce subtle bugs and fragilities. Don't overload these operators naively.
Discussion
The primary reason not to overload operator&&, operator||, or operator, (comma) is
that you cannot implement the full semantics of the built-in operators in these three
cases, and programmers commonly expect those semantics. In particular, the built-in
versions evaluate left-to-right, and for && and || also use short-circuit evaluation.
The built-in versions of && and || first evaluate their left-hand expression, and if that
fully determines the result (false for &&, true for ||) then the right-hand expression
doesn't need to be evaluated—and is guaranteed not to be. We all get so used to this
handy feature that we routinely allow the correctness of the right-hand side depend on
the success of the left-hand side:
Employee* e = TryToGetEmployeeO;
if( e && e->Manager())
This code's correctness relies on the fact that e->Manager() will not be evaluated if e is
null. This is perfectly usual and fine—unless the && used is an overloaded
opera-tor&&, because then the expression involving && will follow function rules
instead:
• Function calls always evaluate all arguments before execution.
• The order of evaluation of function arguments is unspecified. (See also Item 31.)
So let's look at a modernized version of the snippet above that uses smart pointers:
some_smart_ptr<Employee> e = TryToGetEmployeeO;
if( e && e->Manager())
Now, say this code happens to invoke an overloaded operator&& (provided by the
author either of some_smart_ptr or of Employee). Then th