The C++ Programming Language
Bjarne Stroustrup, 3rd edition
Chapter 1 - Notes to the reader
When you program, you create a concrete representation of the ideas in your
solution to some problem. Let the structure of the program reflect those ideas
as directly as possible:
- If you can think of "it" as a separate idea, make it a class.
- If you can think of "it" as a separate entity, make it an object of some
class.
- If two classes have a common interface, make that interface an abstract
class.
- If the implementations of two classes have something significant in common,
make that commonality a base class.
- If a class is a container of objects, make it a template.
- If a function implements an algorithm for a container, make it a template
function implementing the algorithm for a family of containers.
- If a set of classes, templates, etc., are logically related, place them in
a common namespace.
When you define either a class that does not implement a mathematical
entity like a matrix or a complex number or a low-level type such as a linked
list:
- Don't use global data (use members).
- Don't use global functions.
- Don't use public data members.
- Don't use friends, except to avoid [1] or [3].
- Don't put a "type field" in a class; use virtual functions.
- Don't use inline functions, except as a significant optimization.
Chapter 3 - the Standard Library
- Don't reinvent the wheel; use libraries.
- Don't believe in magic; understand what your libraries do, how they
do it, and at what cost they do it.
- When you have a choice, prefer the standard library to other
libraries.
- Do not think that the standard library is ideal for everything.
- Remember to #include the headers for the facilities
you use. [46]
- Remember that standard library facilities are defined in namespace
std. [46]
- Use string rather than char*. [48, 50]
- If in doubt use a range-checked vector (such as Vec). [53]
- Prefer vector<T>, list<T>,
and map<key, value> to T[]. [52, 54, 55]
- When adding elements to a container, use push_back()
or back_inserter(). [54, 56]
- Use push_back() on a vector rather than
realloc() on an array. [56]
- Catch common exceptions in main(). [53]
Chapter 4 - Types and declarations
- Keep scopes small. [82]
- Don't use the same name in both a scope and an enclosing scope.
[82]
- Declare one name (only) per declaration. [80]
- Keep common and local names short, and keep uncommon and-nonlocal
names longer. [81]
- Avoid similar-looking names. [81]
- Maintain a consistent naming style. [81]
- Choose names carefully to reflect meaning rather than
implementation. [81]
- Use a typedef to define a meaningful name for a
built-in type in cases in which the built-in type used to represent a
value n-fight change. [84]
- Use typedefs to define synonyms for types; use
enumerations and classes to define new types. [84]
- Remember that every declaration must specify a type (there is no
"implicit int"). [79]
- Avoid unnecessary assumptions about the numeric value of
characters. [73, 834]
- Avoid unnecessary assumptions about the size of integers. [74]
- Avoid unnecessary assumptions about the range of floating-point
types. [74]
- Prefer a plain int over a short int or a
long int. [74]
- Prefer a double over a float or a
long double. [74]
- Prefer plain char over signed char and
unsigned char. [831]
- Avoid making unnecessary assumptions about the sizes of objects. [74]
- Avoid unsigned arithmetic. [73]
- View signed to unsigned and
unsigned to signed conversions with
suspicion. [835]
- View floating-point to integer conversions with suspicion. [835]
- View conversions to a smaller type, such as int to
char, with suspicion. [835]
Chapter 5 - Pointers, arrays, structures
- Avoid nontrivial pointer arithmetic. [91]
- Take care not to write beyond the bounds of an array. [92.l]
- Use 0 rather than NULL. [88]
- Use vector and valarray rather than
built-in (C-style) arrays. [92]
- Use string rather than zero-terminated arrays of
char. [91]
- Minimize use of plain reference arguments. [97]
- Avoid void* except in low-level code. [100]
- Avoid nontrivial literals ("magic numbers") in code. Instead,
define and use symbolic constants. [76, 94]
Chapter 6 - Expressions and statements
- Prefer the standard library to other libraries and to "handcrafted
code". [119]
- Avoid complicated expressions. [123]
- If in doubt about operator precedence, parenthesize. [123]
- Avoid explicit type conversion (casts). [130]
- When explicit type conversion is necessary, prefer the more
specific cast operators to the C-style cast. [130]
- Use the T(e) notation exclusively for well-defined
construction. [131]
- Avoid expressions with undefined order of evaluation. [122]
- Avoid goto. [137]
- Avoid do-while statements. [136]
- Don't declare a variable until you have a value to initialize it
with. [133, 135, 137]
- Keep comments crisp. [138]
- Maintain a consistent indentation style. [138]
- Prefer defining a member operator new() [421] to
replacing the global operator new(). [129]
- When reading input, always consider ill-formed input. [114]
Chapter 7 - Functions
- Be suspicious of non-const reference arguments; if you want the
function to modify its arguments, use pointers and value return
instead. [97]
- Use const reference arguments when you need to
minimize copying of arguments. [97]
- Use const extensively and consistently. [145]
- Avoid macros. [160]
- Avoid unspecified numbers of arguments. [154]
- Don't return pointers or references to local variables. [148]
- Use overloading when functions perform conceptually the same task on
different types. [149]
- When overloading on integers, provide enough functions to eliminate
common ambiguities. [151]
- When considering the use of a pointer to function, consider whether
a virtual function [36] or a template [41] would be a better
alternative. [156]
Chapter 8 - Namespaces and exceptions
- Use namespaces to express logical structure. [167]
- Place every nonlocal name, except main(), in some
namespace. [167]
- Design a namespace so that you can conveniently use it without
accidentally gaining access to unrelated namespaces. [172]
- Avoid very short names for namespaces. [178]
- If necessary, use namespace aliases to abbreviate long namespace
names. [178]
- Avoid placing heavy notational burdens on users of your
namespaces. [169, 171]
- Use the Namespace::member notation when defining
namespace members. [179]
- Use using namespace only for transition or within a
local scope. [182]
- Use exceptions to decouple the treatment of "errors" from the code
dealing with the ordinary processing. [190]
- Use user-defined rather than built-in types as exceptions. [188]
- Don't use exceptions when local control structures are sufficient.
[192]
Chapter 9 - Source files and programs
- Use header files to represent interfaces and to emphasize logical
structure. [197, 211]
- #include a header in the source file that implements its
functions. [208]
- Don't define global entities with the same name and
similar-but-different meanings in different translation units. [198]
- Avoid non-inline function definitions in headers. [201]
- Use #include only at global scope and in namespaces.
[201]
- #include only complete declarations. [201]
- Use include guards. [216]
- #include C headers in namespaces to avoid global names.
[211]
- Make headers self-contained. [203]
- Distinguish between users' interfaces and implementers' interfaces.
[211]
- Distinguish between average users' interfaces and expert users'
interfaces. [211]
- Avoid nonlocal objects that require run-time initialization in code
intended for use as part of non-C++ programs. [217]
Chapter 10 - Classes
- Represent concepts as classes. [223]
- Use public data (structs) only when it really is just
data and no invariant is meaningful for the data members. [234]
- A concrete type is the simplest kind of class. Where applicable,
prefer a concrete type over more complicated classes and over plain
data structures. [236]
- Make a function a member only if it needs direct access to the
representation of a class. [240]
- Use a namespace to make the association between a class and its
helper functions explicit. [240]
- Make a member function that doesn't modify the value of its object
a const member function. [229]
- Make a function that needs access to the representation of a class
but needn't be called for a specific object a static
member function. [228]
- Use a constructor to establish an invariant for a class. [238]
- If a constructor acquires a resource, its class needs a destructor
to release the resource. [242]
- If a class has a pointer member, it needs copy operations (copy
constructor and copy assignment). [245]
- If a class has a reference member, it probably needs copy
operations (copy constructor and copy assignment). [249]
- If a class needs a copy operation or a destructor, it probably
needs a constructor, a destructor, a copy assignment, and a copy
constructor. [245]
- Check for self-assignment in copy assignments. [245]
- When writing a copy constructor, be careful to copy every element
that needs to be copied (beware of default initializers). [245]
- When adding a new member to a class, always check to see if there
are user-defined constructors that need to be updated to initialize the
member. [249]
- Use enumerators when you need to define integer constants in class
declarations. [248]
- Avoid order dependencies when constructing global and namespace
objects. [252]
- Use first-time switches to minimize order dependencies. [252]
- Remember that temporary objects are destroyed at the end of the
full expression in which they are created. [254]
Chapter 11 - Operator overloading
- Define operators primarily to mimic conventional usage. [261]
- For large operands, use const reference argument
types. [282]
- For large results, consider optimizing the return. [282]
- Prefer the default copy operations if appropriate for a class. [271]
- Redefine or prohibit copying if the default is not appropriate for
a type. [264]
- Prefer member functions over nonmembers for operations that need
access to the representation. [280]
- Prefer nonmember functions over members for operations that do not
need access to the representation. [280]
- Use namespaces to associate helper functions with "their" class.
[265]
- Use nonmember functions for symmetric operators. [268]
- Use () for subscripting multidimensional arrays. [287]
- Make constructors that take a single "size argument"
explicit. [284]
- For non-specialized uses, prefer the standard string
(Chapter 20) to the result of your own exercises. [292]
- Be cautious about introducing implicit conversions. [275]
- Use member functions to express operators that require an lvalue as
its left-hand operand. [272]
Chapter 12 - Derived classes
- Avoid type fields. [308]
- Use pointers and references to avoid slicing. [307]
- Use abstract classes to focus design on the provision of clean
interfaces. [313]
- Use abstract classes to minimize interfaces. [318]
- Use abstract classes to keep implementation details out of
interfaces. [318]
- Use virtual functions to allow new implementations to be added
without affecting user code. [315]
- Use abstract classes to minimize recompilation of user code. [318]
- Use abstract classes to allow alternative implementations to
coexist. [320]
- A class with a virtual function should have a virtual destructor. [318]
- An abstract class typically doesn't need a constructor. [318]
- Keep the representations of distinct concepts distinct. [317]
Chapter 13 - Templates
- Use templates to express algorithms that apply to many argument
types. [334]
- Use templates to express containers. [328]
- Provide specializations for containers of pointers to minimize code
size. [341]
- Always declare the general form of a template before
specializations. [341]
- Declare a specialization before its use. [341]
- Minimize a template definition's dependence on its instantiation
contexts. [333, 859]
- Define every specialization you declare. [341]
- Consider if a template needs specializations for C-style strings
and arrays. [344]
- Parameterize with a policy object. [338]
- Use specialization and overloading to provide a single interface to
implementations of the same concept for different types. [341]
- Provide a simple interface for simple cases and use overloading and
default arguments to express less common cases. [341, 338]
- Debug concrete examples before generalizing to a template. [330]
- Remember to export template definitions that need to
be accessible from other translation units. [350]
- Separately compile large templates and templates with nontrivial
context dependencies. [350]
- Use templates to express conversions but define those conversions
very carefully. [349]
- Where necessary, constrain template arguments using a
constraint() member function. [354 exercise 16]
- Use explicit instantiation to minimize compile time and link time.
[866]
- Prefer a template over derived classes when run-time efficiency is
at a premium. [347]
- Prefer a derived class over a template if adding new variants
without recompilation is important. [347]
- Prefer a template over derived classes when no common base can be
defined. [347]
- Prefer a template over derived classes when built-in types and
structures with compatibility constraints are important. [347]
Chapter 14 - Exception handlling
- Use exceptions for error handling. [355, 374, 383]
- Don't use exceptions where more local control structures will
suffice. [355]
- Use the "resource allocation is initialization" technique to manage
resources. [364]
- Not every program needs to be exception safe. [369]
- Use "resource allocation is initialization" and exception handlers
to maintain invariants. [362]
- Minimize the use of try blocks. Use "resource acquisition is
initialization" instead of explicit handler code. [364]
- Not every function needs to handle every possible error. [383]
- Throw an exception to indicate failure in a constructor. [371]
- Avoid throwing exceptions from copy constructors. [373]
- Avoid throwing exceptions from destructors. [373]
- Have main() catch and report all exceptions. [380]
- Keep ordinary code and error-handling code separate. [369, 374]
- Be sure that every resource acquired in a constructor is released
when throwing an exception in that constructor. [364]
- Keep resource management hierarchical. [364]
- Use exception-specifications for major interfaces. [383]
- Beware of memory leaks caused by memory allocated by new not being
released in case of an exception. [366, 367, 369]
- Assume that every exception that can be thrown by a function will
be thrown. [375]
- Don't assume that every exception is derived from class
exception. [384]
- A library shouldn't unilaterally terminate a program. Instead,
throw an exception and let a caller decide. [355]
- A library shouldn't produce diagnostic output aimed at an end
user. Instead, throw an exception and let a caller decide. [355]
- Develop an error-handling strategy early in a design. [383]
Chapter 15 - Class hierarchies
- Use ordinary multiple inheritance to express a union of features.
[390, 399]
- Use multiple inheritance to separate implementation details from an
interface. [399]
- Use a virtual base to represent something common to
some, but not all, classes in a hierarchy. [399]
- Avoid explicit type conversion (casts). [417]
- Use dynamic_cast where class hierarchy navigation is
unavoidable. [408]
- Prefer dynamic_cast over typeid. [414]
- Prefer private to protected. [405]
- Don't declare data members protected. [405]
- If a class defines operator delete(), it should have a
virtual destructor. [421]
- Don't call virtual functions during construction or destruction. [414]
- Use explicit qualification for resolution of member names sparingly
and preferably use it in overriding functions. [391]
Chapter 16 - Library organization and containers
- Use standard library facilities to maintain portability. [429]
- Don't try to redefine standard library facilities. [431]
- Don't believe that the standard library is best for everything.
- When building a new facility, consider whether it can be presented
within the framework offered by the standard library. [442]
- Remember that standard library facilities are defined in namespace
std. [431]
- Declare standard library facilities by including its header, not by
explicit declaration. [431]
- Take advantage of late abstraction. [435]
- Avoid fat interfaces. [438]
- Prefer algorithms with reverse iterators over explicit loops
dealing with reverse order. [444]
- Use base() to extract an iterator from a
reverse_iterator. [444]
- Pass containers by reference. [447]
- Use iterator types, such as list<char>::iterator,
rather than pointers to refer to elements of a container. [442]
- Use const iterators where you don't need to modify the
elements of a container. [442]
- Use at(), directly or indirectly, if you want range
checking. [445]
- Use push_back() or resize() on a
container rather than realloc() on an array. [450]
- Don't use iterators into a resized vector. [455]
- Use reserve() to avoid invalidating iterators. [455]
- When necessary, use reserve() to make performance
predictable. [455]
Chapter 17 - Standard containers
- By default, use vector when you need a container. [461]
- Know the cost (complexity, big-O measure) of every operation you
use frequently. [464]
- The interface, implementation, and representation of a container
are distinct concepts. Don't confuse them. [465]
- You can sort and search according to a variety of criteria. [467]
- Do not use a C-style string as a key unless you supply a suitable
comparison criterion. [467]
- You can define a comparison criteria so that equivalent, yet
different, key values map to the same key. [467]
- Prefer operations on the end of a sequence
(back-operations) when inserting and deleting elements. [467]
- Use list when you need to do many insertions and
deletions from the front or the middle of a container. [470]
- Use map or multimap when you primarily
access elements by key. [480]
- Use the minimal set of operations to gain maximum flexibility. [462]
- Prefer a map to a hash_map if the elements
need to be kept in order. [497]
- Prefer a hash_map to a map when speed of
lookup is essential. [497]
- Prefer a hash_map to a map if no less-than
operation can be defined for the elements. [497]
- Use find() when you need to check if a key is in an
associative container. [485]
- Use equal_range() to find all elements of a given key
in an associative container. [485]
- Use multimap when several values need to be kept for a
single key. [490]
- Use set or multiset when the key itself is
the only value you need to keep. [491]
Chapter 18 - Algorithms and function objects
- Prefer algorithms to loops. [523]
- When writing a loop, consider whether it could be expressed as a
general algorithm. [508]
- Regularly review the set of algorithms to see if a new application
has become obvious. [508]
- Be sure that a pair of iterator arguments really do specify a
sequence. [512]
- Design so that the most frequently-used operations are simple and
safe. [512]
- Express tests in a form that allows them to be used as predicates. [515]
- Remember that predicates are functions and objects, not types. [515]
- You can use binders to make unary predicates out of binary
predicates. [519]
- Use mem_fun() and mem_fun_ref()
to apply algorithms on containers. [520]
- Use ptr_fun() when you need to bind an argument
of a function. [521]
- Remember that strcmp() differs from == by returning 0
to indicate "equal". [522]
- Use for_each() and transform() only
when there is no more-specific algorithm for a task. [523]
- Use predicates to apply algorithms using a variety of comparison
and equality criteria. [516, 534]
- Use predicates and other function objects so as to use standard
algorithms with a wider range of meanings. [515]
- The default == and < on pointers are rarely
adequate for standard algorithms. [534]
- Algorithms do not directly add or subtract elements from their
argument sequences. [539]
- Be sure that the less-than and equality predicates used on a
sequence match. [534]
- Sometimes, sorted sequences can be used to increase efficiency and
elegance. [539]
- Use qsort() and bsearch() for
compatibility only. [546]
Chapter 19 - Iterators and allocators
- When writing an algorithm, decide which kind of iterator is needed
to provide acceptable efficiency and express the algorithm using the
operators supported by that kind of iterator (only). [550]
- Use overloading to provide more-efficient implementations of an
algorithm when given as arguments iterators that offer more than
minimal support for the algorithm. [553]
- Use iterator_traits to express suitable algorithms
for different iterator categories. [552]
- Remember to use ++ between accesses of
istream_iterators and
ostream_iterators. [558]
- Use inserters to avoid container overflow. [555]
- Use extra checking during debugging and remove checking later only
where necessary. [566]
- Prefer ++p to p++. [565]
- Use uninitialized memory to improve the performance of algorithms
that expand data structures. [574]
- Use temporary buffers to improve the performance of algorithms that
require temporary data structures. [574]
- Think twice before writing your own allocator. [567]
- Avoid malloc(), free(),
realloc(), etc. [577]
- You can simulate a typedef of a template by the
technique used for rebind. [567]
Chapter 20 - Strings
- Prefer string operations to C-style string functions. [599]
- Use strings as variables and members, rather than as base
classes. [582, 768]
- You can pass strings as value arguments and return
them by value to let the system take care of memory management. [587]
- Use at() rather than iterators or [] when
you want range checking. [584, 586]
- Use iterators and [] rather than at()
when you want to optimize speed. [584, 586]
- Directly or indirectly, use substr() to read substrings and
replace() to write substrings. [595, 596]
- Use the find() operations to localize values in a
string (rather than writing an explicit loop). [594]
- Append to a string when you need to add characters
efficiently. [592]
- Use strings as targets of non-time-critical character
input. [598]
- Use string::npos to indicate "the rest of the string".
[586]
- If necessary, implement heavily-used strings using
low-level operations (rather than using low-level data structures
everywhere). [593]
- If you use strings, catch range_error
and out_of_range somewhere. [586]
- Be careful not to pass a char* with the value 0 to a
string function. [589]
- Use c_str to produce a C-style string representation
of a string only when you have to. [589]
- Use isalpha(), isdigit(), etc., when you
need to know the classification of a character rather that writing your
own tests on character values. [601]
Chapter 21 - Streams
- Define << and >> for
user-defined types with values that have meaningful textual
representations. [612, 621]
- Use parentheses when printing expressions containing operators of
low precedence. [607]
- You don't need to modify istream or ostream
to add new << and >> operators. [612]
- You can define a function so that it behaves as a virtual function
based on its second (or subsequent) argument. [612]
- Remember that by default >> skips whitespace. [614]
- Use lower-level input functions such as get() and read()
primarily in the implementation of higher-lever input functions. [618]
- Be careful with the termination criteria when using get(),
getline(), and read(). [618]
- Prefer manipulators to state flags for controlling I/O. [616, 625, 631]
- Use exceptions to catch rare I/O errors (only). [622]
- Tie streams used for interactive I/O. [623]
- Use sentries to concentrate entry and exit code for many functions
in one place. [624]
- Don't use parentheses after a no-argument manipulator. [633]
- Remember to #include when using standard manipulators. [633]
- You can achieve the effect (and efficiency) of a ternary operator
by defining a simple function object. [635]
- Remember that width specifications apply to the following I/O
operation only. [629]
- Remember that precision specifications apply to all following
floating-point output operations. [628]
- Use string streams for in-memory formatting. [640]
- You can specify a mode for a file stream. [637]
- Distinguish sharply between formatting (iostreams) and buffering
(streambufs) when extending the I/O system. [605, 642]
- Implement nonstandard ways of transmitting values as stream
buffers. [645]
- Implement nonstandard ways of formatting values as stream
operations. [612, 621]
- You can isolate and encapsulate calls of user-defined code by using
a pair of functions. [645]
- You can use in_avail() to determine whether an input operation
will block before reading. [645]
- Distinguish between simple operations that need to be efficient and
operations that implement policy (make the former inline and the latter
virtual). [645]
- Use locale to localize "cultural differences". [649]
- Use sync_with_stdio(x) to mix C-style and C++-style I/O and to
disassociate C-style and C++-style I/O. [651]
- Beware of type errors in C-style I/O. [651]
Chapter 22 - Numerics
- Numerical problems are often subtle. If you are not 100% certain about
the mathematical aspects of a numerical problem, either take expert advice
or experiment. [657]
- Use numeric_limits to determine properties of built-in
types. [658]
- Specialize numeric_limits for user-defined scalar types. [658]
- Use valarray for numeric computation when run-time efficiency
is more important than
flexibility with respect to operations and element types. [662]
- Express operations on part of an array in terms of slices rather than
loops. [671]
- Use compositors to gain efficiency through elimination of temporaries
and better algorithms. [675]
- Use std::complex for complex arithmetic. [679]
- You can convert old code that uses a complex class to use the
std::complex template by using a typedef. [679]
- Consider accumulate(), inner_product(),
partial_sum(), and adjacent_difference()
before you write a loop to compute a value from a list. [682]
- Prefer a random-number class for a particular distribution over direct
use of rand(). [685]
- Be careful that your random numbers are sufficiently random. [685]
Chapter 23 - Development and design
- Know what you are trying to achieve. [694]
- Keep in mind that software development is a human activity. [692, 716]
- Proof by analogy is fraud. [692]
- Have specific and tangible aims. [696]
- Don't try technological fixes for sociological problems. [696]
- Consider the longer term in design and in the treatment of people.
[698, 716]
- There is no lower limit to the size of programs for which it is
sensible to design before starting to code. [692]
- Design processes to encourage feedback. [696]
- Don't confuse activity for progress. [694, 696]
- Don't generalize beyond what is needed, what you have direct
experience with, and what can be tested. [698, 700]
- Represent concepts as classes. [700, 702]
- There are properties of a system that should not be represented as
a class. [702]
- Represent hierarchical relationships between concepts as class
hierarchies. [702]
- Actively search for commonality in the concepts of the application
and implementation and represent the resulting more general concepts as
base classes. [702, 707]
- Classifications in other domains are not necessarily useful
classifications in an inheritance model for an application. [702]
- Design class hierarchies based on behavior and invariants. [702,
707, 748]
- Consider use cases. [704]
- Consider using CRC cards. [703]
- Use existing systems as models, as inspiration, and as starting
points. [708]
- Beware of viewgraph engineering. [702]
- Throw a prototype away before it becomes a burden. [710]
- Design for change, focusing on flexibility, extensibility,
portability, and reuse. [700]
- Focus on component design. [701]
- Let each interface represent a concept at a single level of
abstraction. [702]
- Design for stability in the face of change. [700]
- Make designs stable by making heavily-used interfaces minimal,
general, and abstract. [705, 707]
- Keep it small. Don't add features "just in case". [705]
- Always consider alternative representations for a class. If no
alternative representation is plausible, the class is probably not
representing a clean concept. [707]
- Repeatedly review and refine both the design and the
implementation. [696, 701]
- Use the best tools available for testing and for analyzing the
problem, the design, and the implementation. [694, 710]
- Experiment, analyze, and test as early as possible and as often as
possible. [710, 712]
- Don't forget about efficiency. [713]
- Keep the level of formality appropriate to the scale of the
project. [715]
- Make sure that someone is in charge of the overall design. [715]
- Document, market, and support reusable components. [714]
- Document aims and principles as well as details. [712]
- Provide tutorials for new developers as part of the documentation.
[712]
- Reward and encourage reuse of designs, libraries, and classes. [714]
Chapter 24 - Design and programming
- Evolve use towards data abstraction and object-oriented
programming. [724]
- Use C++ features and techniques as needed (only). [724]
- Match design and programming styles. [725]
- Use classes/concepts as a primary focus for design rather than
functions/processing. [725]
- Use classes to represent concepts. [725, 732]
- Use inheritance to represent hierarchical relationships between
concepts (only). [727, 732, 734]
- Express strong guarantees about interfaces in terms of
application-level static types. [727]
- Use program generators and direct-manipulation tools to ease
well-defined tasks. [730]
- Avoid program generators and direct-manipulation tools that do not
interface cleanly with a general-purpose programming language. [730]
- Keep distinct levels of abstraction distinct. [733]
- Focus on component design. [755]
- Make sure that a virtual function has a well-defined meaning and
that every overriding function implements a version of that desired
behavior. [740, 737]
- Use public inheritance to represent "is a" relationships. [740]
- Use membership to represent "has a" relationships. [740]
- Prefer direct membership over a pointer to a separately-allocated
object for expressing simple containment. [738, 740]
- Make sure that the "uses" dependencies are understood, non-cyclic
wherever possible, and minimal. [745]
- Define invariants for all classes. [748]
- Explicitly express preconditions, postconditions, and other
assertions as assertions (possibly using Assert()). [750]
- Define interfaces to reveal the minimal amount of information
needed. [755]
- Minimize an interface's dependencies on other interfaces. [758]
- Keep interfaces strongly typed. [758]
- Express interfaces in terms of application-level types. [758]
- Express an interface so that a request could be transmitted to a
remote server. [758]
- Avoid fat interfaces. [761]
- Use private data and member functions wherever
possible. [758]
- Use the public/protected distinction to distinguish
between the needs of designers of derived classes and general users.
[758]
- Use templates for generic programming. [757]
- Use templates to parameterize an algorithm by a policy. [757]
- Use templates where compile-time type resolution is needed. [757]
- Use class hierarchies where run-time type resolution is needed. [757]
Chapter 25 - Roles of classes
- Make conscious decisions about how a class is to be used (both as a
designer and as a user). [765]
- Be aware of the tradeoffs involved among the different kinds of
classes. [765]
- Use concrete types to represent simple independent concepts. [766]
- Use concrete types to represent concepts where close-to-optimal
efficiency is essential. [766]
- Don't derive from a concrete class. [766]
- Use abstract classes to represent interfaces where the
representation of objects might change. [769]
- Use abstract classes to represent interfaces where different
representations of objects need to coexist. [769]
- Use abstract classes to represent new interfaces to existing types.
[769]
- Use node classes where similar concepts share significant
implementation details. [772]
- Use node classes to incrementally augment an implementation. [772]
- Use Run-Time Type Identification to obtain interfaces from an
object. [774]
- Use classes to represent actions with associated state. [776]
- Use classes to represent actions that need to be stored,
transmitted, or delayed. [776]
- Use interface classes to adapt a class for a new kind of use
(without modifying the class). [778]
- Use interface classes to add checking. [780]
- Use handles to avoid direct use of pointers and references. [782]
- Use handles to manage shared representations. [782]
- Use an application framework where an application domain allows for
the control structure to be predefined. [786]
Appendix B - Features new to C++
- Features primarily for notational convenience:
- // comments (2.3); being added to C
- Support for restricted character sets (C.3. 1)
- Support for extended character sets (C.3.3); being added to C
- Non-constant initializers for objects in static storage (9.4.1)
- const in constant expressions (5.4, C.5)
- Declarations as statements (6.3.1)
- Declarations in for statement initializers and
conditions (6.3.3, 6.3.2.1)
- Structure names need not be prefixed by struct (5.7)
- Features primarily for strengthening the type system:
- Function argument type checking (7.1); later added to C (B.2.2)
- Type-safe linkage (9.2, 9.2.3)
- Free store management using new and delete
(6.2.6, 10.4.5, 15.6)
- const (5.4, 5.4.1); later added to C
- The boolean type bool (4.2)
- New cast syntax (6.2.7)
- Facilities for user-defined types:
- Classes (Chapter 10)
- Member functions (10.2.1) and member classes (11.12)
- Constructors and destructors (10.2.3, 10.4.1)
- Derived classes (Chapter 12, Chapter 15)
- virtual functions and abstract classes (12.2.6, 12.3)
- Public/protected/private access control (10.2.2, 15.3, C.11)
- friends (11.5)
- Pointers to members (15.5, C.12)
- static members (10.2.4)
- mutable members (10.2.7.2)
- Operator overloading (Chapter 11)
- References (5.5)
- Features primarily for program organization (in addition to classes):
- Templates (Chapter 13, C.13)
- Inline functions (7.1.1)
- Default arguments (7.5)
- Function overloading (7.4)
- Namespaces (8.2)
- Explicit scope qualification (operator ::) (4.9.4)
- Exception handling (8.3, Chapter 14)
- Run-time Type Identification (15.4)
Appendix C - Technicalities
- Focus on software development rather than technicalities. [827]
- Adherence to the standard does not guarantee portability. [827]
- Avoid undefined behavior (including proprietary extensions). [827]
- Localize implementation-defined behavior. [827]
- Use keywords and digraphs to represent programs on systems
where { } [ ] | and trigraphs are missing. [829]
- To ease communication, use the ANSI characters to represent
programs. [831]
- Prefer symbolic escape characters to numeric representation of
characters. [830]
- Do not rely on signedness or unsignedness of char. [831]
- If in doubt about the type of an integer literal, use a suffix. [832]
- Avoid value-destroying implicit conversions. [833]
- Prefer vector over array. [836]
- Avoid unions. [841]
- Use fields to represent externally-imposed layouts. [840]
- Be aware of the tradeoffs between different styles of memory
management. [843]
- Don't pollute the global namespace. [847]
- Where a scope (module) rather than a type is needed, prefer a
namespace over a class. [849]
- Remember to define static class template members. [854]
- Use typename to disambiguate type members of a
template parameter. [856]
- Where explicit qualification by template arguments is necessary,
use template to disambiguate template class members. [858]
- Write template definitions with minimal dependence on their
instantiation context. [859]
- If template instantiation takes too long, consider explicit
instantiation. [866]
- If the order of compilation needs to be perfectly predictable,
consider explicit instantiation. [866]