Search for notes by fellow students, in your own course and all over the country.
Browse our notes for titles which look like what you need, you can preview any of the notes via a sample of the contents. After you're happy these are the notes you're after simply pop them into your shopping cart.
Description: This Book contains all the necessary details of C++ Programme and.......all the information about C++ are present in this book......this book a contains all the important programmes that a computer programmer should know............so..........i hope this book is very beneficial for you...........Thank you........
Document Preview
Extracts from the notes are below, to see the PDF you'll receive please use the links above
The
C++
Programming
Language
Fourth Edition
Bjarne Stroustrup
Upper Saddle River, NJ • Boston • Indianapolis • San Francisco
New York • Totonto • Montreal • London • Munich • Paris • Madrid
Capetown • Sydney • Tokyo • Singapore • Mexico City
Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks
...
The author 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
...
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
...
S
...
com
For sales outside the United States, please contact:
International Sales
international@pearsoned
...
com/aw
Library of Congress Cataloging-in-Publication Data
Stroustrup, Bjarne
...
—Fourth edition
...
ISBN 978-0-321-56384-2 (pbk
...
paper)—ISBN 0-321-56384-0 (pbk
...
paper)
1
...
Title
...
73
...
13’3—dc23
2013002159
Copyright © 2013 by Pearson Education, Inc
...
Printed in the United States of America
...
To obtain permission to use material
from this work, please submit a written request to Pearson Education, Inc
...
This book was typeset in Times and Helvetica by the author
...
Second printing, June 2013
Contents
Contents
iii
Preface
v
Preface to the Fourth Edition
...
ix
Preface to the Second Edition
...
xii
Part I: Introductory Material
1
...
3
...
5
...
3
A Tour of C++: The Basics
...
59
A Tour of C++: Containers and Algorithms
...
111
Part II: Basic Facilities
6
...
8
...
10
...
135
Pointers, Arrays, and References
...
201
Statements
...
241
133
iv
Contents
11
...
13
...
15
...
273
Functions
...
343
Namespaces
...
419
Part III: Abstraction Mechanisms
16
...
18
...
20
...
22
...
24
...
26
...
28
...
Classes
...
481
Overloading
...
549
Derived Classes
...
613
Run-Time Type Information
...
665
Generic Programming
...
721
Instantiation
...
759
Metaprogramming
...
827
Part IV: The Standard Library
30
...
32
...
34
...
36
...
38
...
40
...
42
...
44
...
859
STL Containers
...
927
STL Iterators
...
973
Utilities
...
1033
Regular Expressions
...
1073
Locales
...
1159
Concurrency
...
1209
The C Standard Library
...
1267
1281
Preface
All problems in computer science
can be solved by another level of indirection,
except for the problem of too many layers of indirection
...
Wheeler
C++ feels like a new language
...
Furthermore, the resulting programs are better
checked by the compiler and run faster
...
I describe every language feature and standard-library
component that a professional programmer is likely to need
...
• Examples: How can it be used well by itself and in combination with other features? What
are the key techniques and idioms? What are the implications for maintainability and performance?
The use of C++ has changed dramatically over the years and so has the language itself
...
The current ISO
standard C++ (ISO/IEC 14882-2011, usually called C++11) is simply a far better tool for writing
quality software than were previous versions
...
Many answers are not the
same as you would find with 1985, 1995, or 2005 vintage C++: progress happens
...
It is particularly suited for resource-constrained applications, such as
those found in software infrastructures
...
C++ is a language for someone who takes the task of programming seriously
...
There are billions of lines of C++ deployed
...
However, for all applications,
you can do better with modern C++; if you stick to older styles, you will be writing lower-quality
and worse-performing code
...
All code in this book conforms
to the 2011 ISO C++ standard
...
Naturally, these three groups are not disjoint – a professional software developer masters more than
just one programming language
...
If you ask, ‘‘What’s a for-loop?’’ or
‘‘What’s a compiler?’’ then this book is not (yet) for you; instead, I recommend my Programming:
Principles and Practice Using C++ to get started with programming and C++
...
If you ask ‘‘Why bother testing?’’
or say, ‘‘All languages are basically the same; just show me the syntax’’ or are confident that there
is a single language that is ideal for every task, this is not the book for you
...
Language and standard-library facilities for doing systemslevel concurrent programming (e
...
, using multicores)
...
General and uniform initialization, a simpler for-statement, move semantics, basic Unicode support,
lambdas, general constant expressions, control over class defaults, variadic templates, user-defined
literals, and more
...
They are meant to be used in combination –
as bricks in a building set – rather than to be used individually in relative isolation to solve a specific problem
...
In particular,
C++’s design aims to be sufficiently flexible and general to cope with future problems undreamed
of by its designers
...
Boehm, Marshall Clow, Jonathan Coe, Lawrence Crowl,
Walter Daugherty, J
...
Without their help this book would have been much poorer
...
Andrew Sutton is the author of the Origin library, which was the testbed for much of the discussion of emulating concepts in the template chapters, and of the matrix library that is the topic of
Chapter 29
...
’’
Thanks to my graduate design class for finding more problems with the ‘‘tour chapters’’ than
anyone else
...
Every expert
reviewer suggested adding technical details, advanced examples, and many useful development
conventions; every novice reviewer (or educator) suggested adding examples; and most reviewers
observed (correctly) that the book may be too long
...
Brian
Kernighan, for hosting me for part of the sabbatical that gave me time to write this book
...
Andy Hopper, for hosting me for part of the sabbatical that gave me time to write this book
...
College Station, Texas
Bjarne Stroustrup
This page intentionally left blank
Preface to the Third Edition
Programming is understanding
...
C++’s support for design and programming has
improved dramatically over the years, and lots of new helpful techniques have been developed for
its use
...
Ordinary practical programmers have achieved significant
improvements in productivity, maintainability, flexibility, and quality in projects of just about any
kind and scale
...
This book introduces standard C++† and the key programming and design techniques supported
by C++
...
New language features such as namespaces, exceptions,
templates, and run-time type identification allow many techniques to be applied more directly than
was possible before, and the standard library allows the programmer to start from a much higher
level than the bare language
...
This
third edition is the result of a rewrite of even larger magnitude
...
The explosion of C++ use and the massive amount of experience accumulated as a result makes this possible
...
As before, this book presents C++ independently of any particular implementation,
and as before, the tutorial chapters present language constructs and concepts in a ‘‘bottom up’’
order so that a construct is used only after it has been defined
...
Therefore, the standard library can be used to provide realistic and interesting examples well before a reader can be
assumed to understand its inner workings
...
This book presents every major C++ language feature and the standard library
...
However, features are presented in the context of their use
...
x
Preface to the Third Edition
That is, the focus is on the language as the tool for design and programming rather than on the language in itself
...
Except where illustrating technicalities, examples are
taken from the domain of systems software
...
The primary aim of this book is to help the reader understand how the facilities offered by C++
support key programming techniques
...
Only a good understanding of the ideas behind the language facilities leads to
mastery
...
The hope is that this book will help the reader gain
new insights and become a better programmer and designer
...
Without their help and suggestions, this book would have been harder to understand, contained more
errors, been slightly less complete, and probably been a little bit shorter
...
It is slightly unfair to single out individuals, but it would be even more unfair not to mention anyone, so I’d like to especially mention
Mike Ball, Dag Br¨ ck, Sean Corfield, Ted Goldstein, Kim Knuttila, Andrew Koenig, Dmitry
u
Lenkov, Nathan Myers, Martin O’Riordan, Tom Plum, Jonathan Shopiro, John Spicer, Jerry
Schwarz, Alex Stepanov, and Mike Vilot, as people who each directly cooperated with me over
some part of C++ and its standard library
...
I have been able to accommodate many of their suggestions within
the framework of the book so that later printings benefitted significantly
...
In response to requests from readers, I
have added appendices D and E
...
Sevinc, Andy Tenne-Sens, Shoichi Uchida,
u
¸
Ping-Fai (Mike) Yang, and Dennis Yelle
...
– Bilbo Baggins
As promised in the first edition of this book, C++ has been evolving to meet the needs of its users
...
The C++ user-community has grown a hundredfold during the
six years since the first edition of this book; many lessons have been learned, and many techniques
have been discovered and/or validated by experience
...
The primary aim of the language extensions made in the last six years has been to enhance C++
as a language for data abstraction and object-oriented programming in general and to enhance it as
a tool for writing high-quality libraries of user-defined types in particular
...
In this context, safe means that a class provides a specific
type-safe interface between the users of the library and its providers; efficient means that use of the
class does not impose significant overheads in run-time or space on the user compared with handwritten C code
...
Chapters 1 through 10 give a tutorial introduction; Chapters 11 through 13 provide a discussion of design and software development issues; and,
finally, the complete C++ reference manual is included
...
They include refined
overloading resolution, memory management facilities, and access control mechanisms, type-safe
linkage, const and static member functions, abstract classes, multiple inheritance, templates, and
exception handling
...
In addition, C++ is successfully used in many application areas
that are not covered by this label
...
Consequently,
this book describes the C++ language itself without trying to explain a particular implementation,
programming environment, or library
...
’’ This style of exposition allows general principles and useful techniques to stand out more
clearly than they would in a fully elaborated program, where they would be buried in details
...
, are available in ‘‘bulletproof ’’ and/or ‘‘goldplated’’ versions from a
wide variety of commercial and non-commercial sources
...
This edition provides a greater emphasis on tutorial aspects than did the first edition of this
book
...
The discussion of design issues has been greatly
expanded to reflect the demand for information beyond the description of language features and
their immediate use
...
The reference manual, in particular, represents many years of work in this direction
...
In
other words, this book presents the C++ language, its fundamental principles, and the key techniques needed to apply it
...
Many people influenced the development of C++ from 1985
to 1991
...
Also thanks to the many participants of the ‘‘external reviews’’ of the reference manual
drafts and to the people who suffered through the first year of X3J16
...
– B
...
Whorf
C++ is a general purpose programming language designed to make programming more enjoyable
for the serious programmer
...
In addition to the facilities provided by C, C++ provides flexible and efficient facilities for
defining new types
...
This technique for program construction is often called data abstraction
...
Such objects can be used conveniently and safely in contexts in which their type cannot be determined at compile time
...
When
used well, these techniques result in shorter, easier to understand, and easier to maintain programs
...
A class is a user-defined type
...
C++ provides
much better facilities for type checking and for expressing modularity than C does
...
C++ retains C’s ability to deal efficiently with the fundamental
objects of the hardware (bits, bytes, words, addresses, etc
...
C++ and its standard libraries are designed for portability
...
C libraries can be used from a C++ program, and most tools that
support programming in C can be used with C++
...
It provides a complete description of C++, many complete examples, and many
more program fragments
...
In particular, Tom Cargill, Jim Coplien, Stu Feldman, Sandy Fraser,
Steve Johnson, Brian Kernighan, Bart Locanthi, Doug McIlroy, Dennis Ritchie, Larry Rosler, Jerry
Schwarz, and Jon Shopiro provided important ideas for development of the language
...
In addition, hundreds of people contributed to the development of C++ and its compiler by
sending me suggestions for improvements, descriptions of problems they had encountered, and
compiler errors
...
Many people have also helped with the production of this book, in particular, Jon Bentley,
Laura Eaves, Brian Kernighan, Ted Kowalski, Steve Mahaney, Jon Shopiro, and the participants in
the C++ course held at Bell Labs, Columbus, Ohio, June 26-27, 1985
...
It also provides an overview of this book
and explains the approach taken to the description of the language facilities and their
use
...
Chapters
1
2
3
4
5
Notes to the Reader
A Tour of C++: The Basics
A Tour of C++: Abstraction Mechanisms
A Tour of C++: Containers and Algorithms
A Tour of C++: Concurrency and Utilities
2
Introduction
Part I
‘‘
...
Be many people
...
You
have worried too much about Marcus Cocoza, so that you have been really his slave
and prisoner
...
You were always much afraid that Marcus
might do a stupid thing, or be bored
...
I should like you to be easy, your little heart to
be light again
...
’’
– Karen Blixen,
The Dreamers from Seven Gothic Tales (1934)
1
Notes to the Reader
Hurry Slowly
(festina lente)
...
1 The Structure of This Book
A pure tutorial sorts its topics so that no concept is used before it has been introduced; it must be
read linearly starting with page one
...
A pure tutorial can in principle be read without prerequisites – it carefully describes all
...
This book combines aspects of both
...
If not, you can start at the beginning, but try not to
get bogged down in details
...
4
Notes to the Reader
Chapter 1
Making parts of the book relatively self-contained implies some repetition, but repetition also
serves as review for people reading the book linearly
...
Experienced programmers can read the (relatively) quick
‘‘tour’’ of C++ to gain the overview needed to use the book as a reference
...
Chapters 2-5 give a quick introduction to the C++ language
and its standard library
...
Part III
Abstraction Mechanisms: Chapters 16-29 describe C++’s abstraction mechanisms and their use for object-oriented and generic programming
...
1
...
1 Introduction
This chapter, Chapter 1, provides an overview of this book, some hints about how to use it, and
some background information about C++ and its use
...
Please do not feel
obliged to read it all carefully before proceeding
...
Chapter 3
A Tour of C++: Abstraction Mechanisms presents the language features supporting data abstraction, object-oriented programming, and generic programming
...
Chapter 5
A Tour of C++: Concurrency and Utilities outlines the standard-library utilities
related to resource management, concurrency, mathematical computation, regular expressions, and more
...
In particular, it should convince readers that C++ has come a long way since the first, second, and third editions of this book
...
1
...
It introduces the notions of type, object, scope, and storage
...
Modularity – as supported
by namespaces, source files, and exception handling – is also discussed:
Chapter 6
Types and Declarations: Fundamental types, naming, scopes, initialization, simple type deduction, object lifetimes, and type aliases
Section 1
...
2
Basic Facilities
5
Chapter 7
Chapter 8
Chapter 9
Pointers, Arrays, and References
Structures, Unions, and Enumerations
Statements: Declarations as statements, selection statements (if and switch), iteration statements (for, while, and do), goto, and comments
Chapter 10 Expressions: A desk calculator example, survey of operators, constant expressions, and implicit type conversion
...
For example,
I explain the C++ facilities for expressing recursion and iteration, but I do not go into technical
details or spend much time explaining how these concepts are useful
...
Many programmers lack experience with exceptions or
got their experience from languages (such as Java) where resource management and exception handling are not integrated
...
It goes into some detail
about strategy with a focus on the ‘‘Resource Acquisition Is Initialization’’ technique (RAII)
...
1
...
The chapters fall into three rough categories: classes, class hierarchies, and templates
...
Chapter 17 Construction, Cleanup, Copy, and Move shows how a programmer can define the
meaning of creation and initialization of objects of a class
...
Chapter 18 Operator Overloading presents the rules for giving meaning to operators for
user-defined types with an emphasis on conventional arithmetic and logical operators, such as +, ∗, and &
...
’’
6
Notes to the Reader
Chapter 1
Classes can be organized into hierarchies:
Chapter 20 Derived Classes presents the basic language facilities for building hierarchies out
of classes and the fundamental ways of using them
...
The C++ model for access control (public, protected, and private) is presented
...
It also
presents the notion of multiple inheritance, that is, a class having more than one
direct base class
...
We can use dynamic_cast to inquire whether an object of
a base class was defined as an object of a derived class and use the typeid to gain
minimal information from an object (such as the name of its class)
...
Class
templates, function templates, and template aliases are presented
...
The technique of lifting an abstract algorithm from a number of concrete
code examples is central, as is the notion of concepts specifying a generic algorithm’s requirements on its arguments
...
Chapter 26 Instantiation focuses on the rules for name binding
...
Chapter 28 Metaprogramming explores how templates can be used to generate programs
...
Chapter 29 A Matrix Design gives a longish example to show how language features can be
used in combination to solve a complex design problem: the design of an Ndimensional matrix with near-arbitrary element types
...
The presentation technique in Part III differs from that of Part II in that I don’t assume that
the reader knows the techniques described
...
1
...
In particular, they are meant to be
read in any order and can be used as a user-level manual for the library components:
Chapter 30 Standard-Library Overview gives an overview of the standard library, lists the
standard-library headers, and presents language support and diagnostics support,
such as exception and system_error
...
Section 1
...
4
Chapter 32
Chapter 33
Chapter 34
Chapter 35
Chapter 36
Chapter 37
Chapter 38
Chapter 39
Chapter 40
Chapter 41
Chapter 42
Chapter 43
Chapter 44
The Standard Library
7
STL Algorithms presents the algorithms from the STL, including find(), sort(),
and merge()
...
Memory and Resources presents utility components related to memory and
resource management, such as array, bitset, pair, tuple, unique_ptr, shared_ptr,
allocators, and the garbage collector interface
...
Strings documents the string library, including the character traits that are the
basis for the use of different character sets
...
I/O Streams documents the stream I/O library
...
Locales describes class locale and its various facets that provide support for the
handling of cultural differences in character sets, formatting of numeric values,
formatting of date and time, and more
...
Concurrency presents the C++ basic memory model and the facilities offered for
concurrent programming without locks
...
The C Standard Library documents the C standard library (including printf() and
clock()) as incorporated into the C++ standard library
...
1
...
5 Examples and References
This book emphasizes program organization rather than the design of algorithms
...
A trivial algorithm is typically better suited to
illustrate an aspect of the language definition or a point about program structure
...
Often, reimplementation with a
more suitable algorithm is an exercise
...
Textbook examples necessarily give a warped view of software development
...
I see no substitute for
writing realistically sized programs in order to get an impression of what programming and a
8
Notes to the Reader
Chapter 1
programming language are really like
...
These are the basic techniques from which every program is composed
...
The selection of examples reflects my background in compilers, foundation libraries, and simulations
...
Examples are simplified versions of what is found in real code
...
My ideal is the shortest and clearest example that
illustrates a design principle, a programming technique, a language construct, or a library feature
...
For purely language-technical
examples, I use variables named x and y, types called A and B, and functions called f() and g()
...
The language features presented and the detail in which
they are described roughly reflect my view of what is needed for effective use of C++
...
An
understanding of every language-technical detail of a language feature or library component is neither necessary nor sufficient for writing good programs
...
What is
needed is an understanding of design and programming techniques together with an appreciation of
application domains
...
The final arbiter of language and
standard-library rules is the ISO C++ standard [C++,2011]
...
3
...
5
...
1 (ISO C++ standard, §5
...
1)
...
g
...
g
...
To save a few trees and to simplify additions, the hundreds of exercises for this book have been
moved to the Web
...
stroustrup
...
The language and library used in this book are ‘‘pure C++’’ as defined by the C++ standard
[C++,2011]
...
The
major program fragments in this book were tried using several C++ implementations
...
However, I
see no point in mentioning which implementations failed to compile which examples
...
See Chapter 44 for suggestions on how to
cope with older C++ compilers and with code written for C compilers
...
For example, I prefer
{}-style initializers and using for type aliases
...
’’ However, being startled is often a good way to start reviewing material
...
Obviously, if you have to use a pre-C++11 compiler (say, because some of your customers have
not yet upgraded to the current standard), you have to refrain from using novel features
...
§44
...
Section 1
...
2 The Design of C++
The purpose of a programming language is to help express ideas in code
...
The first purpose ideally requires a language that is ‘‘close to the
machine’’ so that all important aspects of a machine are handled simply and efficiently in a way
that is reasonably obvious to the programmer
...
The second purpose ideally requires a language that is ‘‘close to the problem to be solved’’
so that the concepts of a solution can be expressed directly and concisely
...
Thus, C++ is based on the
idea of providing both
• direct mappings of built-in operations and types to hardware to provide efficient memory
use and efficient low-level operations, and
• affordable and flexible abstraction mechanisms to provide user-defined types with the same
notational support, range of uses, and performance as built-in types
...
Over the years, further application
of these simple ideals resulted in a far more general, efficient, and flexible set of facilities
...
The design of C++ has focused on programming techniques dealing with fundamental notions
such as memory, mutability, abstraction, resource management, expression of algorithms, error handling, and modularity
...
By defining libraries of classes, class hierarchies, and templates, you can write C++ programs at
a much higher level than the one presented in this book
...
4
...
For high-level applications programming to be effective and convenient, we need libraries
...
That’s true for every general-purpose
language
...
My standard introduction of C++ used to start:
• C++ is a general-purpose programming language with a bias toward systems programming
...
What has changed over the years is an increase in the importance, power, and
flexibility of C++’s abstraction mechanisms:
• C++ is a general-purpose programming language providing a direct and efficient model of
hardware combined with facilities for defining lightweight abstractions
...
By general-purpose programming language I mean a language designed to support a wide variety
of uses
...
No language is ideal for every application and every programmer,
but the ideal for C++ is to support the widest possible range of application areas well
...
In particular, the implementation of
software infrastructure (e
...
, device drivers, communications stacks, virtual machines, operating
systems, operations systems, programming environments, and foundation libraries) is mostly systems programming
...
Of course, you can also program in ways that completely hide hardware, use expensive abstractions (e
...
, every object on the free store and every operation a virtual function), use inelegant styles
(e
...
, overabstraction), or use essentially no abstractions (‘‘glorified assembly code’’)
...
The Design and Evolution of C++ book [Stroustrup,1994] (known as D&E) outlines the ideas
and design aims of C++ in greater detail, but two principles should be noted:
• Leave no room for a lower-level language below C++ (except for assembly code in rare
cases)
...
• What you don’t use you don’t pay for
...
Therefore, a language feature and
a fundamental abstraction must be designed not to waste a single byte or a single processor
cycle compared to equivalent alternatives
...
These are Draconian principles, but essential in some (but obviously not all) contexts
...
The STL is an example (§4
...
1, §4
...
5, Chapter 31, Chapter 32,
Chapter 33)
...
1
...
1 Programming Style
Languages features exist to provide support for programming styles
...
The general ideals for design and programming can be expressed simply:
• Express ideas directly in code
...
• Represent relationships among ideas directly in code
...
• Express simple ideas simply
...
A fundamental reason for that is that a language embodies a set of engineering tradeoffs
reflecting differing needs, tastes, and histories of various individuals and communities
...
Section 1
...
1
Programming Style
11
The C++ language features most directly support four programming styles:
• Procedural programming
• Data abstraction
• Object-oriented programming
• Generic programming
However, the emphasis is on the support of effective combinations of those
...
) solution to most nontrivial problems tends to be one
that combines aspects of these styles
...
For example, what I
refer to as a ‘‘programming style,’’ others call a ‘‘programming technique’’ or a ‘‘paradigm
...
I feel
uncomfortable with the word ‘‘paradigm’’ as pretentious and (from Kuhn’s original definition) having implied claims of exclusivity
...
• Procedural programming: This is programming focused on processing and the design of
suitable data structures
...
C++’s support comes in the form of the built-in types, operators, statements, functions, structs, unions, etc
...
Compared to C, C++ provides further support for procedural programming in the
form of many additional language constructs and a stricter, more flexible, and more supportive type system
...
C++ supports concrete and
abstract classes
...
The notion of an
abstract class provides direct support for complete data hiding
...
In addition to allowing the definition lattices of classes, C++
provides a variety of features for navigating class lattices and for simplifying the definition
of a class out of existing ones
...
3
...
2) and encapsulation (§20
...
5)
...
Here, ‘‘general’’ means that an algorithm can be designed to accept a
wide variety of types as long as they meet the algorithm’s requirements on its arguments
...
Templates provide (compiletime) parametric polymorphism
...
Thus, C++ could be (and has been) called class oriented
...
Focusing exclusively on one of these styles is a mistake: except for toy examples, doing so leads to
wasted development effort and suboptimal (inflexible, verbose, poorly performing, unmaintainable,
etc
...
12
Notes to the Reader
Chapter 1
I wince when someone characterizes C++ exclusively through one of these styles (e
...
, ‘‘C++ is
an object-oriented language’’) or uses a term (e
...
, ‘‘hybrid’’ or ‘‘mixed paradigm’’) to imply that a
more restrictive language would be preferable
...
The styles mentioned are not distinct alternatives: each contributes techniques to a more
expressive and effective style of programming, and C++ provides direct language support for their
use in combination
...
Even the earliest published account of C++ [Stroustrup,1982] presents examples that use these different styles in combination and presents language features aimed at supporting such combinations:
• Classes support all of the mentioned styles; all rely on the user representing ideas as userdefined types or objects of user-defined types
...
• Member functions, constructors, destructors, and user-defined assignment provide a clean
functional interface to objects as needed by data abstraction and object-oriented programming
...
More
general overloading had to wait until 1984 and uniform initialization until 2010
...
They are necessary for overloading
...
• Generic functions and parameterized types (generated from functions and classes using
macros) support generic programming
...
• Base and derived classes provide the foundation for object-oriented programming and some
forms of data abstraction
...
• Inlining made the use of these facilities affordable in systems programming and for building
run-time and space efficient libraries
...
Today’s C++ provides much better support for design and programming based on
lightweight abstraction, but the aim of elegant and efficient code was there from the very beginning
...
The fundamental object in C++ has identity; that is, it is located in a specific location in memory and can be distinguished from other objects with (potentially) the same value by comparing
addresses
...
4)
...
In C++11, this notion of rvalue
has been developed into a notion of a value that can be moved around cheaply (§3
...
2, §6
...
1,
§7
...
2)
...
This nicely complements the techniques and language features (e
...
, lambda expressions) developed primarily for
generic programming
...
g
...
Section 1
...
1
Programming Style
13
From the very earliest days, C++ programs and the design of C++ itself have been concerned
about resource management
...
2),
• perfect (no leaks are acceptable), and
• statically type-safe
...
Foundation and application libraries beyond the standard
provided many more examples, such as Matrix and Widget
...
This was soon backed with the ability to control copy by defining
assignment as well as copy constructors
...
3) in C++11 completes this line of thinking by allowing cheap movement of potentially
large objects from scope to scope (§3
...
2) and to simply control the lifetime of polymorphic or
shared objects (§5
...
1)
...
Any class that establishes and maintains an invariant relies on a subset of those features
...
2
...
For this reason, restricting language features with the intent of eliminating programmer errors is, at best, dangerous
...
Good design and the
absence of errors cannot be guaranteed merely by the presence or absence of specific language features
...
The notion of static types and compile-time type checking is central to effective use of C++
...
Following Simula, the design of user-defined types with interfaces that are checked at compile time is key to the
expressiveness of C++
...
C++ type-checking and data-hiding features rely on compile-time analysis of programs to prevent accidental corruption of data
...
They can, however, be used freely without incurring run-time or space overheads
...
C++’s static type system is flexible, and the use of simple user-defined types implies little, if
any overhead
...
A type-rich style of programming makes code more
14
Notes to the Reader
Chapter 1
readable, maintainable, and analyzable
...
C++ compilers and development tools support such type-based analysis [Stroustrup,2012]
...
However, my ideal is (and always was) complete type safety
...
’’ Note that Simula was both
type-safe and flexible
...
’’ However, the list of solid reasons against basing my work on type-safe
Algol68 [Woodward,1974] was long and painful
...
But it is an ideal that C++ programmers (especially library
builders) can strive for
...
Outside of low-level sections of code (hopefully
isolated by type-safe interfaces), code that interfaces to code obeying different language conventions (e
...
, an operating system call interface), and the implementations of fundamental abstractions
(e
...
, string and vector), there is now little need for type-unsafe code
...
2
...
The main reasons for relying on C were to build on a proven set of low-level language
facilities and to be part of a technical community
...
The continuing, more or less parallel evolution of C
and C++ has been a constant source of concern and requires constant attention [Stroustrup,2002]
...
In particular, there are differences in opinion as
to the value of compatibility, differences in opinion on what constitutes good programming, and
differences in opinion on what support is needed for good programming
...
One hundred percent C/C++ compatibility was never a goal for C++ because that would compromise type safety and the smooth integration of user-defined and built-in types
...
C++98 adopted many details from C89 (§44
...
1)
...
C’s facilities for low-level systems programming tasks are retained and enhanced; for
example, see inlining (§3
...
1
...
1
...
2
...
2
...
4, §12
...
6)
...
g
...
The definition of C++ has been revised to ensure that a construct that is both legal C and legal
C++ has the same meaning in both languages (§44
...
One of the original aims for C was to replace assembly coding for the most demanding systems
programming tasks
...
2
...
The difference between C and C++ is primarily in the degree of emphasis on types and structure
...
Through extensive use of the type system, C++ is even more
expressive without loss of performance
...
Programming in C encourages many techniques and tricks that are rendered unnecessary by C++ language features
...
3
...
However, good
C programs tend to be C++ programs
...
Experience with
any statically typed language will be a help when learning C++
...
2
...
C++ has no built-in high-level data types
and no high-level primitive operations
...
If a user wants such
a type, it can be defined in the language itself
...
A well-designed userdefined type differs from a built-in type only in the way it is defined, not in the way it is used
...
) provides many examples
of such types and their uses
...
Except for a few unfortunate and unimportant historical accidents, the C++ standard library is written in C++
...
This ensures that they can be used in large systems that typically consist of layer
upon layer of abstraction
...
For
example, constructs that would make it necessary to store ‘‘housekeeping information’’ in every
object were rejected, so if a user declares a structure consisting of two 16-bit quantities, that structure will fit into a 32-bit register
...
This
can be essential for embedded and high-performance applications
...
So, programmers of such applications don’t have to work with a low-level
(error-prone, impoverished, and unproductive) set of language features
...
Fortunately, C++ was never restricted
to UNIX; it simply used UNIX and C as a model for the relationships among language, libraries,
compilers, linkers, execution environments, etc
...
There are, however, good reasons for using C++ in environments that provide significantly more run-time support
...
16
Notes to the Reader
Chapter 1
Not every piece of code can be well structured, hardware-independent, easy to read, etc
...
It also possesses facilities for hiding
such code behind elegant and safe interfaces
...
C++’s emphasis on modularity, strongly typed interfaces, and flexibility pays off here
...
This book emphasizes techniques for providing general-purpose facilities, generally useful
types, libraries, etc
...
Furthermore, because all nontrivial programs consist of many semi-independent parts, the techniques for writing such parts serve programmers of all applications
...
This introduces library components and their underlying design concepts and implementation techniques
...
However, if the standard library provides a component that addresses a problem, it is almost always
better to use that component than to build your own
...
Over the
longer term, the standard component (possibly accessed through a convenient custom interface) is
likely to lower long-term maintenance, porting, tuning, and education costs
...
With C++,
this is not so
...
, is typically a
bit shorter than the equivalent C program not using these facilities
...
C++ supports systems programming
...
The idea of writing all software in a
single language is a fantasy
...
By that, I meant that a C++, C, assembler, or Fortran
function could call functions in the other languages without extra overhead or conversion of data
structures passed among them
...
The use of multiple processes and
multiple address spaces relied on (extralinguistic) operating system support
...
Initially, I relied on the UNIX Shell for that, but just about
any ‘‘scripting language’’ will do
...
C++ was designed to be part of large, concurrent, multilanguage systems
...
3
Learning C++
17
1
...
Fortunately, a programming language does not have to be
perfect to be a good tool for building great systems
...
What is perfect for one task is
often seriously flawed for another because perfection in one area implies specialization
...
Not everything can be expressed directly using the built-in features of a language
...
Language features exist to support a variety of programming styles and techniques
...
Writing programs is essential; understanding a programming language is not just an intellectual
exercise
...
In practical programming, there is little advantage in knowing the most obscure language features or using the largest number of features
...
Only in the context provided by techniques and by other features does the feature acquire
meaning and interest
...
No significant system is built exclusively in terms of the language features themselves
...
We use libraries to improve maintainability, portability, and performance
...
g
...
Many of the most fundamental programming concepts are represented in the standard
library
...
The standard library
is the repository of much hard-earned knowledge of how to use C++ well
...
This has surprised some who – correctly – point
out that C++ isn’t the smallest or cleanest language ever designed
...
The most important thing to do when learning C++ is to focus on fundamental concepts (such
as type safety, resource management, and invariants) and programming techniques (such as
resource management using scoped objects and the use of iterators in algorithms) and not get lost in
language-technical details
...
For this, an appreciation of programming and design techniques is far more
important than understanding all the details
...
18
Notes to the Reader
Chapter 1
C++ programming is based on strong static type checking, and most techniques aim at achieving a high level of abstraction and a direct representation of the programmer’s ideas
...
To gain the benefits of C++, programmers coming to it from a different language must
learn and internalize idiomatic C++ programming style and technique
...
Thoughtlessly applying techniques effective in one language to another typically leads to awkward, poorly performing, and hard-to-maintain code
...
’’ You can write in the style of Fortran, C, Lisp, Java,
etc
...
Every language can be a fertile source of ideas about how to write C++ programs
...
Over the basic type system of a language, only
Pyrrhic victories are possible
...
C++ is safer and more expressive, and it reduces the need to
focus on low-level techniques
...
Chapter 44 is
a guide for programmers going from C++ to C, say, to deal with legacy code
...
There are several independently developed implementations of C++
...
To help master all of this you
can find textbooks, manuals, and a bewildering variety of online resources
...
Each has its own
emphasis and bias, so use at least two
...
3
...
Imitate good writing
...
The main ideal for C++ programming – as for programming in most higher-level languages – is
to express concepts (ideas, notions, etc
...
We try to ensure that the
concepts we talk about, represent with boxes and arrows on our whiteboard, and find in our (nonprogramming) textbooks have direct and obvious counterparts in our programs:
[1] Represent ideas directly in code
...
g
...
[3] Represent independent ideas independently in code
...
Section 1
...
1
Programming in C++
19
More specifically:
[5] Prefer statically type-checked solutions (when applicable)
...
g
...
[7] Don’t overabstract (i
...
, don’t generalize, introduce class hierarchies, or parameterize
beyond obvious needs and experience)
...
3
...
1
...
2 Suggestions for C++ Programmers
By now, many people have been using C++ for a decade or two
...
Often, what an experienced C++ programmer has failed to notice over the
years is not the introduction of new features as such, but rather the changes in relationships
between features that make fundamental new programming techniques feasible
...
You find out only by reexamining the basics
...
If you already know the contents of a chapter, you can be
done in minutes
...
I learned a fair bit writing this book, and I suspect that hardly any C++ programmer knows
every feature and technique presented
...
Through its organization and examples,
this book offers such a perspective
...
4
...
2, §13
...
2
...
[2] Use constructor/destructor pairs to simplify resource management (RAII; §5
...
3)
...
2
...
2, §11
...
1)
...
4, §4
...
4, Chapter 32)
...
2
...
[6] Use exceptions, rather than error codes, to report errors that cannot be handled locally
(§2
...
3, §13
...
[7] Use move semantics to avoid copying large objects (§3
...
2, §17
...
2)
...
2
...
[9] Use shared_ptr to reference shared objects, that is, objects without a single owner that is
responsible for their destruction (§5
...
1)
...
2)
...
3
...
3
...
1
...
3 Suggestions for C Programmers
The better one knows C, the harder it seems to be to avoid writing C++ in C style, thereby losing
many of the potential benefits of C++
...
20
Notes to the Reader
Chapter 1
[1]
Don’t think of C++ as C with a few features added
...
To get really major advantages from C++ as compared to C, you need to
apply different design and implementation styles
...
[3] Use the C++ standard library as a teacher of new techniques and programming styles
...
g
...
[4] Macro substitution is almost never necessary in C++
...
5), constexpr (§2
...
3,
§10
...
4) to define manifest constants, inline (§12
...
5) to avoid
function-calling overhead, templates (§3
...
4
...
3
...
[5] Don’t declare a variable before you need it, and initialize it immediately
...
3), in for-statement initializers (§9
...
4
...
[6] Don’t use malloc()
...
2) does the same job better, and instead of
realloc(), try a vector (§3
...
2)
...
2
...
2, §11
...
1)
...
Their use limits the support you can get from the type system and can harm performance
...
If you must use an
explicit type conversion, try using one of the named casts (e
...
, static_cast; §11
...
2) for a
more precise statement of what you are trying to do
...
C++ standard-library strings (§4
...
2
...
4
...
In general, try not to build yourself what has
already been provided by the standard library
...
g
...
[10] Do not assume that something laboriously written in C style (avoiding C++ features such
as classes, templates, and exceptions) is more efficient than a shorter alternative (e
...
,
using standard-library facilities)
...
To obey C linkage conventions, a C++ function must be declared to have C linkage (§15
...
5)
...
3
...
Their aims are significantly different and so are many of their application domains
...
To use C++
well, you need to adopt programming and design techniques appropriate to C++, rather than trying
to write Java in C++
...
Section 1
...
4
Suggestions for Java Programmers
21
[2]
Use the C++ abstraction mechanisms (e
...
, classes and templates): don’t fall back to a C
style of programming out of a false feeling of familiarity
...
[4] Don’t immediately invent a unique base for all of your classes (an Object class)
...
[5] Minimize the use of reference and pointer variables: use local and member variables
(§3
...
1
...
2, §16
...
4, §17
...
[6] Remember: a variable is never implicitly a reference
...
[8] A function is not virtual by default
...
[9] Use abstract classes as interfaces to class hierarchies; avoid ‘‘brittle base classes,’’ that is,
base classes with data members
...
[11] Use a constructor to establish a class invariant (and throw an exception if it can’t)
...
g
...
Don’t imitate finally (doing so is more ad hoc and in the longer run far
more work than relying on destructors)
...
g
...
g
...
[14] Use freestanding functions (nonmember functions) to minimize coupling (e
...
, see the
standard algorithms), and use namespaces (§2
...
2, Chapter 14) to limit the scope of freestanding functions
...
5
...
1)
...
[17] C++ offers only the most minimal run-time reflection: dynamic_cast and typeid (Chapter
22)
...
g
...
Most of this advice applies equally to C# programmers
...
4 History
I invented C++, wrote its early definitions, and produced its first implementation
...
C++ was designed to provide Simula’s facilities for program organization [Dahl,1970]
[Dahl,1972] together with C’s efficiency and flexibility for systems programming [Kernighan,1978]
[Kernighan,1988]
...
The class concept (with derived classes and virtual functions) was borrowed from it
...
22
Notes to the Reader
Chapter 1
The evolution of C++ was always in the context of its use
...
In particular, my colleagues at
AT&T Bell Laboratories were essential for the growth of C++ during its first decade
...
Furthermore, it does not go into details
...
My two papers from the ACM History of Programming Languages conference and my
Design and Evolution of C++ book (known as ‘‘D&E’’) describe the design and evolution of C++
in detail and document influences from other programming languages
...
In my FAQ, I try to maintain a connection between the standard facilities and the people
who proposed and refined those facilities [Stroustrup,2010]
...
1
...
1 Timeline
The work that led to C++ started in the fall of 1979 under the name ‘‘C with Classes
...
The initial feature set included classes and derived
classes, public/private access control, constructors and destructors, and function declarations with argument checking
...
1984 ‘‘C with Classes’’ was renamed to C++
...
1985 First commercial release of C++ (October 14)
...
1985 The C++ Programming Language (‘‘TC++PL,’’ October 14) [Stroustrup,1986]
...
1991 The C++ Programming Language, Second Edition [Stroustrup,1991], presenting generic
programming using templates and error handling based on exceptions (including the
‘‘Resource Acquisition Is Initialization’’ general resource management idiom)
...
The standard
library added the STL framework of generic containers and algorithms
...
2002 Work on a revised standard, colloquially named C++0x, started
...
A C++ Technical Report
introduced new standard-library components, such as regular expressions, unordered containers (hash tables), and resource management pointers, which later became part of
C++0x
...
Section 1
...
1
Timeline
23
2009 C++0x was feature complete
...
The standard library added several components, including
threads, locks, and most of the components from the 2003 Technical Report
...
2012 The first complete C++11 implementations emerged
...
2013 The C++ Programming Language, Fourth Edition introduced C++11
...
As is not uncommon in large projects, we
were overly optimistic about the completion date
...
4
...
For that, I needed some event-driven simulations for which Simula would have been
ideal, except for performance considerations
...
The result of adding Simula-style
classes to C, ‘‘C with Classes,’’ was used for major projects in which its facilities for writing programs that use minimal time and space were severely tested
...
The first
use of C++ outside a research organization started in July 1983
...
The name signifies the evolutionary nature of the changes from C; ‘‘++’’ is the C increment operator
...
Connoisseurs
of C semantics find C++ inferior to ++C
...
For yet another interpretation of the name
C++, see the appendix of [Orwell,1949]
...
Its main purpose was to make writing good
programs easier and more pleasant for the individual programmer
...
There was
no ‘‘C++ project’’ either, or a ‘‘C++ design committee
...
1
...
2
...
I used
macros to provide primitive parameterization
...
Late that year, I was
24
Notes to the Reader
Chapter 1
able to present a set of language facilities supporting a coherent set of programming styles; see
§1
...
1
...
In the terminology of the time, ‘‘a constructor creates the execution environment for the member
functions and the destructor reverses that
...
If there were other languages at the time that supported multiple constructors capable of executing general code, I didn’t (and don’t) know of them
...
C++ was released commercially in October 1985
...
1
...
2
...
2
...
5, §16
...
9), function overloading (§12
...
7), operator
overloading (§3
...
1
...
2
...
3
...
Of these
features, support for run-time polymorphism in the form of virtual functions was by far the most
controversial
...
Systems programmers tended to view indirect function
calls with suspicion, and people acquainted with other languages supporting object-oriented programming had a hard time believing that virtual functions could be fast enough to be useful in systems code
...
The resistance to virtual functions may be related to a resistance to
the idea that you can get better systems through more regular structure of code supported by a programming language
...
My view was (and is) that
we need every bit of help we can get from languages and tools: the inherent complexity of the systems we are trying to build is always at the edge of what we can express
...
In the early years,
the feedback from Stu Feldman, Alexander Fraser, Steve Johnson, Brian Kernighan, Doug McIlroy,
and Dennis Ritchie was invaluable
...
The most important of those were templates [Stroustrup,1988] and exception handling
[Koenig,1990], which were considered experimental at the time the standards effort started
...
At the time, nobody knew how to simultaneously get all three, and to compete with C-style code
for demanding systems applications, I felt that I had to choose the first two properties
...
The design of exceptions focused on
multilevel propagation of exceptions, the passing of arbitrary information to an error handler, and
the integrations between exceptions and resource management by using local objects with destructors to represent and release resources (what I clumsily called ‘‘Resource Acquisition Is Initialization’’; §13
...
I generalized C++’s inheritance mechanisms to support multiple base classes [Stroustrup,1987a]
...
I
considered it far less important than templates or exceptions
...
Section 1
...
2
...
For example, I designed the complex [Stroustrup,1984], vector, stack, and (I/O) stream
[Stroustrup,1985] classes together with the operator overloading mechanisms
...
Jonathan’s
string and list classes were the first to see extensive use as part of a library
...
The task library described in [Stroustrup,1987b] was part of the first ‘‘C with Classes’’ program ever written in 1980
...
Unfortunately, we had to wait until 2011
(30 years!) to get concurrency support standardized and universally available (§1
...
4
...
3, Chapter 41)
...
C++ grew up in an environment with a multitude of established and experimental programming
languages (e
...
, Ada [Ichbiah,1979], Algol 68 [Woodward,1974], and ML [Paulson,1996])
...
However, the determining influences always came from
the applications I encountered
...
1
...
3 The 1998 Standard
The explosive growth of C++ use caused some changes
...
The result was a conscious effort to maintain contact
between implementers of C++ compilers and major users
...
AT&T Bell Labs made a major contribution to C++ and its wider community by allowing me to
share drafts of revised versions of the C++ reference manual with implementers and users
...
A less enlightened company
could have caused major problems of language fragmentation simply by doing nothing
...
Their names can be found in The Annotated C++ Reference Manual (‘‘the
ARM’’) [Ellis,1989]
...
In June 1991, this ANSI (American national) standardization of C++
became part of an ISO (international) standardization effort for C++ and named WG21
...
I served on these committees throughout
...
An initial draft standard for public review was produced in April 1995
...
A ‘‘bug
fix release’’ of this standard was issued in 2003, so you sometimes hear people refer to C++03, but
that is essentially the same language as C++98
...
4
...
1 Language Features
By the time the ANSI and ISO standards efforts started, most major language features were in place
and documented in the ARM [Ellis,1989]
...
The template mechanisms, in particular, benefited from much
detailed work
...
At the initiative of Dmitry Lenkov from Hewett-Packard, minimal facilities to use run-time type information (RTTI; Chapter 22) were introduced
...
I tried to get a facility
for optional conservative garbage collection accepted, but failed
...
Clearly, the 1998 language was far superior in features and in particular in the detail of specification to the 1989 language
...
In addition to the
inevitable minor mistakes, two major features were added that in retrospect should not have been:
• Exception specifications provide run-time enforcement of which exceptions a function is
allowed to throw
...
Exception specifications turned out to be worse than useless for improving readability, reliability, and performance
...
The 2011 standard introduced noexcept (§13
...
1
...
• It was always obvious that separate compilation of templates and their uses would be ideal
[Stroustrup,1994]
...
After a long debate in the committee, a compromise was
reached and something called export templates were specified as part of the 1998 standard
...
We are still looking for a solution
...
Thus, export solved the wrong problem
...
3) may help by providing precise specification of template requirements
...
1
...
3
...
4, §4
...
It was the work of Alex Stepanov (with Dave Musser, Meng Le, and others) based
on more than a decade’s work on generic programming
...
The STL has been massively influential
within the C++ community and beyond
...
I had failed to ship a sufficiently large foundation library with Release 1
...
0
...
4
...
2
The Standard Library
27
time the standards work started
...
g
...
The standard-library string (§4
...
The valarray library for numerical computation (§40
...
Jerry Schwarz transformed my streams library (§1
...
2
...
3, Chapter 38) using Andrew Koenig’s manipulator technique (§38
...
5
...
The
iostreams library was further refined during standardization, where the bulk of the work was done
by Jerry Schwarz, Nathan Myers, and Norihiro Kumagai
...
For example, there is no standard
GUI, database access library, or Web application library
...
The reasons for that are practical and commercial, rather than technical
...
1
...
4 The 2011 Standard
The current C++, C++11, known for years as C++0x, is the work of the members of WG21
...
These processes probably led to a better (and more rigorous) specification, but they also limited innovation
[Stroustrup,2007]
...
The second
ISO C++ standard (ISO/IEC 14882-2011) [C++,2011] was ratified by a 21-0 national vote in
August 2011
...
Consequently, serious work on
new language features did not start until 2002
...
In terms of pages of standards text, the language grew by
about 30% and the standard library by about 100%
...
Also, the work on a new C++ standard obviously had
to take great care not to compromise older code through incompatible changes
...
The overall aims for the C++11 effort were:
• Make C++ a better language for systems programming and library building
...
The aims are documented and detailed in [Stroustrup,2007]
...
This involved a memory model (§41
...
3),
which is primarily the work of Hans Boehm, Brian McKnight, and others
...
Pete Becker, Peter Dimov, Howard Hinnant, William Kempf, Anthony
Williams, and others did massive amounts of work on that
...
3
...
Concurrency is an
area where a complete and detailed listing of who did what and why would require a very long
paper
...
1
...
4
...
2
...
By ‘‘better’’ I mean easier to read, easier to write, more elegant, less error-prone, more maintainable, faster-running, consuming fewer resources, etc
...
3
...
6
...
6
...
• Deducing the type of an object from its initializer, auto: §2
...
2, §6
...
6
...
I first designed and implemented auto in 1983 but had to remove it because of C compatibility problems
...
2
...
4, §12
...
6; Gabriel Dos Reis and Bjarne Stroustrup [DosReis,2010]
...
4
...
• Inheriting constructors: §20
...
5
...
• Lambda expressions, a way of implicitly defining function objects at the point of their use in
an expression: §3
...
3, §11
...
• Move semantics, a way of transmitting information without copying: §3
...
2, §17
...
2;
Howard Hinnant
...
5
...
1; David Abrahams, Rani Sharoni, and Doug Gregor
...
2
...
• The range-for statement: §2
...
5, §9
...
1; Thorsten Ottosen and Bjarne Stroustrup
...
3
...
Alisdair Meredith, Chris Uzdavinis, and Ville
Voutilainen
...
In particular, a
way of defining a template by binding some arguments of another template: §3
...
5, §23
...
• Typed and scoped enumerations: enum class: §8
...
1; David E
...
• Universal and uniform initialization (including arbitrary-length initializer lists and protection against narrowing): §2
...
2, §3
...
1
...
3
...
3
...
3
...
• Variadic templates, a mechanism for passing an arbitrary number of arguments of arbitrary
types to a template: §3
...
4, §28
...
Section 1
...
4
...
The technical reports to the
committee [WG21] and my C++11 FAQ [Stroustrup,2010a] give many of the names
...
The reason my name appears so often is (I
hope) not vanity, but simply that I chose to work on what I consider important
...
Their major role is to flesh out the C++ feature set to better
support programming styles (§1
...
1)
...
Much work went into a proposal that did not make it into the standard
...
g
...
It was designed, specified, implemented, and tested, but by a large majority the committee
decided that the proposal was not yet ready
...
However, the committee decided against ‘‘concepts’’ on the grounds of complexity, difficulty of use, and compile-time performance [Stroustrup,2010b]
...
’’ This is
currently a field of active research and design [Sutton,2011] [Stroustrup,2012a]
...
4
...
2 Standard Library
The work on what became the C++11 standard library started with a standards committee technical
report (‘‘TR1’’)
...
As for language features, I’ll only list a few standard-library components with references to the
text and the names of the individuals most closely associated with them
...
2
...
Some components, such as unordered_map (hash tables), were ones we simply didn’t
manage to finish in time for the C++98 standard
...
Boost is a volunteer organization
created to provide useful library components based on the STL [Boost]
...
4
...
• The basic concurrency library components, such as thread, mutex, and lock: §5
...
2; Pete
Becker, Peter Dimov, Howard Hinnant, William Kempf, Anthony Williams, and more
...
3
...
4
...
• The garbage collection interface: §34
...
• A regular expression library, regexp: §5
...
• A random number library: §5
...
3, §40
...
It was about time
...
Several utility components were tried out in Boost:
• A pointer for simply and efficiently passing resources, unique_ptr: §5
...
1, §34
...
1; Howard
E
...
This was originally called move_ptr and is what auto_ptr should have been had
we known how to do so for C++98
...
2
...
3
...
A
successor to the C++98 counted_ptr proposal from Greg Colvin
...
4
...
5, §34
...
4
...
They credit a
long list of contributors, including Doug Gregor, David Abrahams, and Jeremy Siek
...
5
...
His acknowledgments list a veritable who’s who
of Boost (including Doug Gregor, John Maddock, Dave Abrahams, and Jaakko Jarvi)
...
5
...
He credits William
Kempf and others with contributions
...
4
...
You don’t usually see it
...
C++ is used by millions of programmers in essentially every application domain
...
This massive use is supported by
half a dozen independent implementations, many thousands of libraries, hundreds of textbooks, and
dozens of websites
...
Early applications tended to have a strong systems programming flavor
...
Many current ones (e
...
, Windows, Apple’s OS,
Linux, and most portable-device OSs) have key parts done in C++
...
I consider uncompromising low-level efficiency essential
for C++
...
In such code, predictability of performance
is at least as important as raw speed
...
C++
was designed so that every language feature is usable in code under severe time and space constraints (§1
...
4) [Stroustrup,1994,§4
...
Some of today’s most visible and widely used systems have their critical parts written in C++
...
Many other programming languages
and technologies depend critically on C++’s performance and reliability in their implementation
...
g
...
g
...
g
...
g
...
NET Web
services framework)
...
Most applications have sections of code that are critical for acceptable performance
...
For most code, maintainability, ease of extension, and ease of testing are key
...
Examples
are financial systems, telecommunications, device control, and military applications
...
S
...
e
...
Many
such applications are large and long-lived
...
Multimillion-line C++ programs are common
...
4
...
Thus, games has been
another major applications area for C++
...
g
...
g
...
g
...
g
...
C++ wasn’t specifically designed with numerical computation in mind
...
A major reason for this is that traditional numerical work must often be combined with graphics and with computations relying on
data structures that don’t fit into the traditional Fortran mold (e
...
, [Root,1995])
...
C++’s ability to be used effectively for applications that require work in a variety of application
areas is an important strength
...
Traditionally, such application
areas were considered distinct and were served by distinct technical communities using a variety of
programming languages
...
It is
designed so that C++ code can coexist with code written in other languages
...
Furthermore, no really major system is written 100% in a single language
...
Major applications are not written in just the raw language
...
There are many thousands of
C++ libraries, so keeping up with them all is impossible
...
5 Advice
Each chapter contains an ‘‘Advice’’ section with a set of concrete recommendations related to its
contents
...
A piece of advice
should be applied only where reasonable
...
I find rules of the form ‘‘never do this’’ unhelpful
...
Negative suggestions tend not to be phrased as absolute prohibitions
and I try to suggest alternatives
...
The ‘‘Advice’’ sections do not contain explanations
...
For starters, here are a few high-level recommendations derived from the sections on design,
learning, and history of C++:
32
Notes to the Reader
Chapter 1
[1]
Represent ideas (concepts) directly in code, for example, as a function, a class, or an enumeration; §1
...
[2] Aim for your code to be both elegant and efficient; §1
...
[3] Don’t overabstract; §1
...
[4] Focus design on the provision of elegant and efficient abstractions, possibly presented as
libraries; §1
...
[5] Represent relationships among ideas directly in code, for example, through parameterization or a class hierarchy; §1
...
1
...
2
...
[7] C++ is not just object-oriented; §1
...
1
...
2
...
[9] Prefer solutions that can be statically checked; §1
...
1
...
2
...
4
...
1
...
2
...
[12] Use libraries, especially the standard library, rather than trying to build everything from
scratch; §1
...
1
...
2
...
[14] Low-level code is not necessarily efficient; don’t avoid classes, templates, and standardlibrary components out of fear of performance problems; §1
...
4, §1
...
3
...
3
...
[16] C++ is not just C with a few extensions; §1
...
3
...
You are not going to get
it right the first time
...
6 References
[Austern,2003]
[Barron,1963]
[Barton,1994]
[Berg,1995]
[Boehm,2008]
[Boost]
[Budge,1992]
Matt Austern et al
...
Software – Practice & Experience
...
November 2003
...
W
...
: The main features of CPL
...
6 (2):
134
...
comjnl
...
org/content/6/2/134
...
pdf+html
...
J
...
R
...
Addison-Wesley
...
1994
...
William Berg, Marshall Cline, and Mike Girou: Lessons Learned from the
OS/400 OO Project
...
Vol
...
10
...
Hans-J
...
Adve: Foundations of the C++ concurrency
memory model
...
The Boost library collection
...
boost
...
Kent Budge, J
...
Perry, and A
...
Robinson: High-Performance Scientific
Computation Using C++
...
USENIX C++ Conference
...
August 1992
...
6
[C,1990]
[C,1999]
[C,2011]
[C++,1998]
[C++Math,2010]
[C++,2011]
[Campbell,1987]
[Coplien,1995]
[Cox,2007]
[Czarnecki,2000]
[Dahl,1970]
[Dahl,1972]
[Dean,2004]
[Dechev,2010]
[DosReis,2006]
[DosReis,2010]
[DosReis,2011]
[Ellis,1989]
[Freeman,1992]
[Friedl,1997]:
References
33
X3 Secretariat: Standard – The C Language
...
ISO Standard
ISO/IEC 9899-1990
...
Washington, DC
...
Standard – The C Language
...
ISO/IEC 9899
...
X3J11/90-013-2011
...
ISO/IEC 14882:1998
...
ISO/IEC 29124:2010
...
ISO/IEC 14882:2011
...
: The Design of a Multiprocessor Operating System
...
USENIX C++ Conference
...
November 1987
...
Coplien: Curiously Recurring Template Patterns
...
February 1995
...
January
2007
...
com/˜rsc/regexp/regexp1
...
K
...
Eisenecker: Generative Programming: Methods, Tools,
and Applications
...
Reading, Massachusetts
...
ISBN
0-201-30977-7
...
Dahl, B
...
Nygaard: SIMULA Common Base Language
...
Oslo, Norway
...
O-J
...
A
...
Hoare: Hierarchical Program Construction in Structured Programming
...
New York
...
J
...
Ghemawat: MapReduce: Simplified Data Processing on Large
Clusters
...
2004
...
Dechev, P
...
Stroustrup: Understanding and Effectively
Preventing the ABA Problem in Descriptor-based Lock-free Designs
...
May 2010
...
POPL06
...
Gabriel Dos Reis and Bjarne Stroustrup: General Constant Expressions for
System Programming Languages
...
The 25th ACM Symposium
On Applied Computing
...
Gabriel Dos Reis and Bjarne Stroustrup: A Principled, Complete, and Efficient Representation of C++
...
Vol
...
2011
...
Ellis and Bjarne Stroustrup: The Annotated C++ Reference
Manual
...
Reading, Mass
...
ISBN 0-201-51459-1
...
Prentice
Hall
...
1992
...
Jeffrey E
...
Friedl: Mastering Regular Expressions
...
Sebastopol, California
...
ISBN 978-1565922570
...
: Design Patterns: Elements of Reusable Object-Oriented
Software
...
Reading, Massachusetts
...
ISBN
0-201-63361-2
...
: Concepts: Linguistic Support for Generic Programming in C++
...
John L
...
Patterson: Computer Architecture, Fifth Edition: A Quantitative Approach
...
San Francisco, California
...
ISBN 978-0123838728
...
Ichbiah et al
...
SIGPLAN Notices
...
14, No
...
June 1979
...
Kamath, Ruth E
...
Smith: Reaping Benefits
with Object-Oriented Technology
...
Vol
...
5
...
Brian W
...
Ritchie: The C Programming Language
...
Englewood Cliffs, New Jersey
...
Brian W
...
Ritchie: The C Programming Language,
Second Edition
...
Englewood Cliffs, New Jersey
...
ISBN
0-13-110362-8
...
Knuth: The Art of Computer Programming
...
Reading, Massachusetts
...
Andrew Koenig and Bjarne Stroustrup: C++: As close to C as possible – but
no closer
...
Vol
...
7
...
A
...
Koenig and B
...
Proc USENIX C++ Conference
...
Joseph C
...
NASA/TM-2002-211716
...
Addison-Wesley
...
ISBN 978-0201183955
...
McKenney: Is Parallel Programming Hard, And, If So, What Can
You Do About It? kernel
...
Corvallis, Oregon
...
http://kernel
...
html
...
Regex
...
boost
...
2009
...
Secker and Warburg
...
1949
...
Paulson: ML for the Working Programmer
...
Cambridge
...
ISBN 0-521-56543-X
...
Pirkelbauer, Y
...
Stroustrup: Design and Evaluation of
C++ Open Multi-Methods
...
Elsevier
Journal
...
doi:10
...
scico
...
06
...
Martin Richards and Colin Whitby-Strevens: BCPL – The Language and Its
Compiler
...
Cambridge
...
ISBN
0-521-21965-5
...
root
...
ch
...
6
[Rozier,1988]
[Siek,2000]
[Solodkyy,2012]
[Stepanov,1994]
[Stewart,1998]
[Stroustrup,1982]
[Stroustrup,1984]
[Stroustrup,1985]
[Stroustrup,1986]
[Stroustrup,1987]
[Stroustrup,1987b]
[Stroustrup,1988]
[Stroustrup,1991]
[Stroustrup,1993]
[Stroustrup,1994]
[Stroustrup,1997]
[Stroustrup,2002]
[Stroustrup,2007]
References
35
Web address
...
Rozier et al
...
Computing Systems
...
1, No
...
Fall 1988
...
Siek and Andrew Lumsdaine: Concept checking: Binding parametric polymorphism in C++
...
First Workshop on C++ Template Programming
...
2000
...
Solodkyy, G
...
Stroustrup: Open and Efficient Type Switch
for C++
...
OOPSLA’12
...
HP
Labs Technical Report HPL-94-34 (R
...
1994
...
W
...
Basic Decompositions
...
Philadelphia, Pennsylvania
...
B
...
Sigplan Notices
...
The first public description of ‘‘C with
Classes
...
Stroustrup: Operator Overloading in C++
...
IFIP WG2
...
September 1984
...
Stroustrup: An Extensible I/O Facility for C++
...
Summer 1985
USENIX Conference
...
Stroustrup: The C++ Programming Language
...
Reading, Massachusetts
...
ISBN 0-201-12078-X
...
Stroustrup: Multiple Inheritance for C++
...
EUUG Spring Conference
...
B
...
Shopiro: A Set of C Classes for Co-Routine Style Programming
...
USENIX C++ Conference
...
November 1987
...
Stroustrup: Parameterized Types for C++
...
USENIX C++ Conference, Denver
...
B
...
Addison-Wesley
...
1991
...
B
...
Proc
...
ACM Sigplan Notices
...
1993
...
Stroustrup: The Design and Evolution of C++
...
Reading, Mass
...
ISBN 0-201-54330-3
...
Stroustrup: The C++ Programming Language, Third Edition
...
Reading, Massachusetts
...
ISBN 0-201-88954-4
...
2000
...
B
...
The C/C++ Users Journal
...
www
...
com/papers
...
B
...
ACM HOPL-III
...
36
Notes to the Reader
[Stroustrup,2008]
Chapter 1
B
...
Addison-Wesley
...
ISBN 0-321-54372-6
...
Stroustrup: The C++11 FAQ
...
stroustrup
...
html
...
Stroustrup: The C++0x ‘‘Remove Concepts’’ Decision
...
Dobb’s Journal
...
[Stroustrup,2012a] B
...
Sutton: A Concept Design for the STL
...
January 2012
...
Stroustrup: Software Development for Infrastructure
...
January
2012
...
1109/MC
...
353
...
Sutton and B
...
Proc
...
July 2011
...
Tanenbaum: Modern Operating Systems, Third Edition
...
Upper Saddle River, New Jersey
...
ISBN 0-13-600663-9
...
: Minimizing Dependencies within Generic Classes for
Faster and Smaller Programs
...
October 2009
...
0
...
Reading, Massachusetts
...
ISBN 0-201-48345-9
...
Research Version,
Tenth Edition
...
February
1985
...
Josuttis: C++ Templates: The Complete
Guide
...
2002
...
[Veldhuizen,1995]
Todd Veldhuizen: Expression Templates
...
June 1995
...
Veldhuizen: C++ Templates are Turing Complete
...
2003
...
ACM Transactions on Mathematical Software, Vol
...
1
...
[WG21]
ISO SC22/WG21 The C++ Programming Language Standards Committee:
Document Archive
...
open-std
...
[Williams,2012]
Anthony Williams: C++ Concurrency in Action – Practical Multithreading
...
ISBN 978-1933988771
...
Wilson and Paul Lu (editors): Parallel Programming Using C++
...
Cambridge, Mass
...
ISBN 0-262-73118-5
...
Addison-Wesley
...
1999
...
[Woodward,1974]
P
...
Woodward and S
...
Bond: Algol 68-R Users Guide
...
London
...
2
A Tour of C++: The Basics
The first thing we do, let’s
kill all the language lawyers
...
1 Introduction
The aim of this chapter and the next three is to give you an idea of what C++ is, without going into
a lot of details
...
These are the language facilities supporting the styles most often seen in C and sometimes called procedural programming
...
Chapter 4 and
Chapter 5 give examples of standard-library facilities
...
If not, please consider reading a textbook, such as Programming: Principles and Practice Using C++ [Stroustrup,2009], before continuing here
...
If you find this ‘‘lightning tour’’
confusing, skip to the more systematic presentation starting in Chapter 6
...
For example, loops are not
discussed in detail until Chapter 10, but they will be used in obvious ways long before that
...
As an analogy, think of a short sightseeing tour of a city, such as Copenhagen or New York
...
You do not know the city after such a
tour
...
To really know a city, you have to live in
it, often for years
...
After the tour, the
real exploration can begin
...
Consequently, it
does not identify language features as present in C, part of C++98, or new in C++11
...
4 and Chapter 44
...
2 The Basics
C++ is a compiled language
...
A
C++ program typically consists of many source code files (usually simply called source files)
...
When we talk about portability of C++ programs, we usually
mean portability of source code; that is, the source code can be successfully compiled and run on a
variety of systems
...
g
...
g
...
g
...
g
...
That is, the C++ standard library can be implemented in C++ itself (and is with very
minor uses of machine code for things such as thread context switching)
...
C++ is a statically typed language
...
g
...
The type of an object determines
the set of operations applicable to it
...
2
...
2
...
4)
...
Here, they indicate the start and end of the function
body
...
A comment is for
the human reader; the compiler ignores comments
...
The program starts
by executing that function
...
’’ If no value is returned, the system will receive a value indicating successful completion
...
Not every operating system and execution
environment make use of that return value: Linux/Unix-based environments often do, but Windows-based environments rarely do
...
Here is a program that writes Hello, World!:
#include
int main()
{
std::cout << "Hello, World!\n";
}
The line #include
stream I/O facilities as found in iostream
...
The operator << (‘‘put to’’) writes its second argument onto its first
...
A string
literal is a sequence of characters surrounded by double quotes
...
’’ In this case, \n is the
newline character, so that the characters written are Hello, World! followed by a newline
...
4
...
I usually leave out the std:: when discussing standard features; §2
...
2 shows how to
make names from a namespace visible without explicit qualification
...
For example:
#include
using namespace std;
double square(double x)
{
return x∗x;
}
// make names from std visible without std:: (§2
...
2)
// square a double precision floating-point number
40
A Tour of C++: The Basics
Chapter 2
void print_square(double x)
{
cout << "the square of " << x << " is " << square(x) << "\n";
}
int main()
{
print_square(1
...
234 is 1
...
2
...
2 Types, Variables, and Arithmetic
Every name and every expression has a type that determines the operations that may be performed
on it
...
A declaration is a statement that introduces a name into the program
...
• An object is some memory that holds a value of some type
...
• A variable is a named object
...
For example:
bool
char
int
double
// Boolean, possible values are true and false
// character, for example, 'a', ' z', and '9'
// integer, for example, 1, 42, and 1066
// double-precision floating-point number, for example, 3
...
0
Each fundamental type corresponds directly to hardware facilities and has a fixed size that determines the range of values that can be stored in it:
bool:
char:
int:
double:
A char variable is of the natural size to hold a character on a given machine (typically an 8-bit
byte), and the sizes of other types are quoted in multiples of the size of a char
...
e
...
The arithmetic operators can be used for appropriate combinations of these types:
Section 2
...
2
x+y
+x
x−y
−x
x∗y
x/y
x%y
Types, Variables, and Arithmetic
41
// plus
// unar y plus
// minus
// unar y minus
// multiply
// divide
// remainder (modulus) for integers
So can the comparison operators:
x==y
x!=y
x
x<=y
x>=y
// equal
// not equal
// less than
// greater than
// less than or equal
// greater than or equal
In assignments and in arithmetic operations, C++ performs all meaningful conversions (§10
...
3)
between the basic types so that they can be mixed freely:
void some_function()
{
double d = 2
...
C++ offers a variety of notations for expressing initialization, such as the
universal form based on curly-brace-delimited initializer lists:
=
used above, and a
double d1 = 2
...
3};
complex
complex
complex
// a complex number with double-precision floating-point scalars
vector
// a vector of ints
// the = is optional with {
...
3
...
2)
...
5):
int i1 = 7
...
2};
int i3 = {7
...
2
...
Don’t introduce a name until you have a suitable value for it
...
2
...
1)
...
2;
auto z = sqrt(y);
// a bool
// a char
// an int
// a double
// z has the type of whatever sqr t(y) returns
With auto, we use the = syntax because there is no type conversion involved that might cause problems (§6
...
6
...
We use auto where we don’t have a specific reason to mention the type explicitly
...
• We want to be explicit about a variable’s range or precision (e
...
, double rather than float)
...
This is especially important in
generic programming where the exact type of an object can be hard for the programmer to know
and the type names can be quite long (§4
...
1)
...
3), C++ offers more specific operations for modifying a variable:
x+=y
++x
x−=y
−−x
x∗=y
x/=y
x%=y
// x = x+y
// increment: x = x+1
// x = x-y
// decrement: x = x-1
// scaling: x = x*y
// scaling: x = x/y
// x = x%y
These operators are concise, convenient, and very frequently used
...
2
...
5):
• const: meaning roughly ‘‘I promise not to change this value’’ (§7
...
This is used primarily
to specify interfaces, so that data can be passed to functions without fear of it being modified
...
• constexpr: meaning roughly ‘‘to be evaluated at compile time’’ (§10
...
This is used primarily to specify constants, to allow placement of data in memory where it is unlikely to be corrupted, and for performance
...
4∗square(dmv);
constexpr double max2 = 1
...
4∗square(var);
// dmv is a named constant
// var is not a constant
// OK if square(17) is a constant expression
// error : var is not a constant expression
// OK, may be evaluated at run time
Section 2
...
3
double sum(const vector
vector
...
4, 4
...
2
...
For example:
constexpr double square(double x) { return x∗x; }
To be
constexpr, a function must be rather simple: just a return-statement computing a value
...
We allow a constexpr function to be called with non-constant-expression argu-
ments in contexts that do not require constant expressions, so that we don’t have to define essentially the same function twice: once for constant expressions and once for variables
...
g
...
2
...
3), case labels (§2
...
4, §9
...
2), some template arguments (§25
...
In other cases, compile-time evaluation is important for performance
...
4)
...
2
...
For example,
here is a simple function that prompts the user and returns a Boolean indicating the response:
bool accept()
{
cout << "Do you want to proceed (y or n)?\n";
char answer = 0;
cin >> answer;
// write question
// read answer
if (answer == 'y') return true;
return false;
}
To match the << output operator (‘‘put to’’), the >> operator (‘‘get from’’) is used for input; cin is
the standard input stream
...
The \n character at the end
of the output string represents a newline (§2
...
1)
...
\n";
return false;
}
}
A switch-statement tests a value against a set of constants
...
If no default is provided, no
action is taken if the value doesn’t match any case constant
...
For example, we might like to give the user a few tries
to produce acceptable input:
bool accept3()
{
int tries = 1;
while (tries<4) {
cout << "Do you want to proceed (y or n)?\n";
char answer = 0;
cin >> answer;
// write question
// read answer
switch (answer) {
case 'y':
return true;
case 'n':
return false;
default:
cout << "Sorry, I don't understand that
...
\n";
return false;
}
The while-statement executes until its condition becomes false
...
2
...
’’ All arrays have
0
as their lower
Section 2
...
5
Pointers, Arrays, and Loops
45
bound, so v has six elements, v[0] to v[5]
...
2
...
A pointer variable can hold the address of an object of the appropriate type:
char∗ p = &v[3];
char x = ∗p;
// p points to v’s four th element
// *p is the object that p points to
In an expression, prefix unary ∗ means ‘‘contents of’’ and prefix unary & means ‘‘address of
...
}
This for-statement can be read as ‘‘set i to zero; while i is not 10, copy the ith element and increment
i
...
C++ also offers
a simpler for-statement, called a range-for-statement, for loops that traverse a sequence in the simplest way:
void print()
{
int v[] = {0,1,2,3,4,5,6,7,8,9};
for (auto x : v)
cout << x << '\n';
// for each x in v
for (auto x : {10,21,32,43,54,65})
cout << x << '\n';
//
...
’’ Note that we don’t have to specify an array bound when we initialize it
with a list
...
4
...
If we didn’t want to copy the values from v into the variable x, but rather just have x refer to an
element, we could write:
46
A Tour of C++: The Basics
Chapter 2
void increment()
{
int v[] = {0,1,2,3,4,5,6,7,8,9};
for (auto& x : v)
++x;
//
...
’’ A reference is similar to a pointer,
except that you don’t need to use a prefix ∗ to access the value referred to by the reference
...
When used in declarations, operators (such as &, ∗, and []) are called declarator operators:
T a[n];
T∗ p;
T& r;
T f(A);
// T[n]: array of n Ts (§7
...
2)
// T&: reference to T (§7
...
2
...
When
we don’t have an object to point to or if we need to represent the notion of ‘‘no object available’’
(e
...
, for an end of a list), we give the pointer the value nullptr (‘‘the null pointer’’)
...
The definition of count_x() assumes that the char∗ is a C-style string, that is, that the pointer
points to a zero-terminated array of char
...
2
...
However, using nullptr
eliminates potential confusion between integers (such as 0 or NULL) and pointers (such as nullptr)
...
3
User-Defined Types
47
2
...
2
...
2
...
2
...
C++’s set of built-in types and operations is
rich, but deliberately low-level
...
However, they don’t provide the programmer with high-level facilities to conveniently write advanced applications
...
The C++ abstraction mechanisms are primarily designed to let programmers design
and implement their own types, with suitable representations and operations, and for programmers
to simply and elegantly use such types
...
They are referred to as classes and enumerations
...
The rest of
this chapter presents the simplest and most fundamental facilities for that
...
Chapter 4 and Chapter 5 present an overview of the standard library, and since the standard library
mainly consists of user-defined types, they provide examples of what can be built using the language facilities and programming techniques presented in Chapter 2 and Chapter 3
...
3
...
A variable of type Vector can be defined like this:
Vector v;
However, by itself that is not of much use because v’s elem pointer doesn’t point to anything
...
For example, we can construct a Vector like this:
void vector_init(Vector& v, int s)
{
v
...
sz = s;
}
That is, v’s elem member gets a pointer produced by the new operator and v’s size member gets the
number of elements
...
2
...
7); that way, vector_init() can modify the vector passed to it
...
2)
...
elem[i];
// read into elements
double sum = 0;
for (int i=0; i!=s; ++i)
sum+=v
...
In particular, a user of Vector has to know every detail of Vector’s representation
...
Chapter 4 presents the standard-library vector, which contains many nice improvements, and Chapter 31 presents the complete vector in the context of other standard-library facilities
...
Don’t reinvent standard-library components, such as vector and string; use them
...
(dot) to access struct members through a name (and through a reference) and −> to
access struct members through a pointer
...
sz;
// access through name
int i2 = rv
...
3
...
However, a tighter connection between the representation and the
operations is needed for a user-defined type to have all the properties expected of a ‘‘real type
...
To do that we have
to distinguish between the interface to a type (to be used by all) and its implementation (which has
access to the otherwise inaccessible data)
...
A
class is defined to have a set of members, which can be data, function, or type members
...
For example:
Section 2
...
2
Classes
class Vector {
public:
Vector(int s) :elem{new double[s]}, sz{s} { }
double& operator[](int i) { return elem[i]; }
int size() { return sz; }
private:
double∗ elem; // pointer to the elements
int sz;
// the number of elements
};
49
// construct a Vector
// element access: subscripting
Given that, we can define a variable of our new type Vector:
Vector v(6);
// a Vector with 6 elements
We can illustrate a Vector object graphically:
Vector:
elem:
sz:
0:
1:
2:
3:
4:
5:
6
Basically, the Vector object is a ‘‘handle’’ containing a pointer to the elements (elem) plus the number of elements (sz)
...
2
...
3)
...
This is the basic technique for
handling varying amounts of information in C++: a fixed-size handle referring to a variable amount
of data ‘‘elsewhere’’ (e
...
, on the free store allocated by new; §11
...
How to design and use such
objects is the main topic of Chapter 3
...
The read_and_sum()
example from §2
...
1 simplifies to:
double read_and_sum(int s)
{
Vector v(s);
for (int i=0; i!=v
...
size(); ++i)
sum+=v[i];
return sum;
// make a vector of s elements
// read into elements
// take the sum of the elements
}
A ‘‘function’’ with the same name as its class is called a constructor, that is, a function used to construct objects of a class
...
3
...
Unlike an
ordinary function, a constructor is guaranteed to be used to initialize objects of its class
...
50
A Tour of C++: The Basics
Chapter 2
Vector(int) defines how objects of type Vector are constructed
...
That integer is used as the number of elements
...
Then, we initialize sz to s
...
It returns a reference
to the appropriate element (a double&)
...
Obviously, error handling is completely missing, but we’ll return to that in §2
...
3
...
2
...
2
shows how to use a destructor to elegantly do that
...
3
...
g
...
For example, Color::red is Color’s red
which is different from Traffic_light::red
...
They are used to make code
more readable and less error-prone than it would have been had the symbolic (and mnemonic) enumerator names not been used
...
Being separate types, enum classes help prevent accidental misuses of constants
...
4
...
By default, an enum class has only assignment, initialization, and comparisons (e
...
, == and <;
§2
...
2) defined
...
3
...
4
...
2
...
2
...
3, §3
...
2
...
4, Chapter 23)
...
The first and most important step is to distinguish between the interface to a part and
its implementation
...
A declaration specifies all that’s needed to use a function or a type
...
’’ For this
example, we might like for the representation of Vector to be ‘‘elsewhere’’ also, but we will deal
with that later (abstract types; §3
...
2)
...
algorithm as found in math textbook
...
However,
that makes no real difference: a library is simply some ‘‘other code we happen to use’’ written with
the same language facilities as we use
...
4
...
The definitions of those types and functions are in separate source files and compiled separately
...
Such separation can be used to minimize compilation times and to strictly enforce separation of logically distinct parts of a program (thus minimizing the chance of errors)
...
g
...
Typically, we place the declarations that specify the interface to a module in a file with a name
indicating its intended use
...
h:
class Vector {
public:
Vector(int s);
double& operator[](int i);
int size();
private:
double∗ elem;
// elem points to an array of sz doubles
int sz;
};
This declaration would be placed in a file
file, to access that interface
...
h,
and users will include that file, called a header
// user
...
h"
#include
using namespace std;
// get Vector’s interface
// get the the standard-librar y math function interface including sqrt()
// make std members visible (§2
...
2)
Section 2
...
1
Separate Compilation
double sqrt_sum(Vector& v)
{
double sum = 0;
for (int i=0; i!=v
...
h file providing its interface:
...
cpp:
#include "Vector
...
cpp and Vector
...
h,
but the two files are otherwise independent and can be separately compiled
...
h:
Vector
user
...
h"
use Vector
interface
Vector
...
h"
define Vector
Strictly speaking, using separate compilation isn’t a language issue; it is an issue of how best to
take advantage of a particular language implementation
...
The best approach is to maximize modularity, represent that modularity logically through
language features, and then exploit the modularity physically through files for effective separate
compilation (Chapter 14, Chapter 15)
...
4
...
2
...
3
...
4),
C++ offers namespaces (Chapter 14) as a mechanism for expressing that some declarations belong
together and that their names shouldn’t clash with other names
...
2
...
1, §18
...
4):
namespace My_code {
class complex { /*
...
int main();
}
int My_code::main()
{
complex z {1,2};
auto z2 = sqrt(z);
std::cout << '{' << z2
...
imag() << "}\n";
//
...
1
...
The precaution is wise, because the standard
library does provide support for complex arithmetic (§3
...
1
...
4)
...
g
...
The ‘‘real main()’’ is defined in the global namespace,
that is, not local to a defined namespace, class, or function
...
2
...
They
simplify the composition of a program out of separately developed parts
...
4
...
However, C++ provides a few features to
help
...
Instead of painstakingly building up our applications
from the built-in types (e
...
, char, int, and double) and statements (e
...
, if, while, and for), we build
more types that are appropriate for our applications (e
...
, string, map, and regex) and algorithms
(e
...
, sort(), find_if(), and draw_all())
...
g
...
4
...
The majority of C++ constructs are
dedicated to the design and implementation of elegant and efficient abstractions (e
...
, user-defined
types and algorithms using them)
...
As programs grow, and especially when libraries are used extensively,
standards for handling errors become important
...
4
...
1 Exceptions
Consider again the Vector example
...
3
...
• The user of Vector cannot consistently detect the problem (if the user could, the out-of-range
access wouldn’t happen in the first place)
...
The user can then take appropriate action
...
To do that, the implementation will unwind the
function call stack as needed to get back to the context of that caller (§13
...
1)
...
try { // exceptions here are handled by the handler defined below
v[v
...
handle range error
...
}
We put code for which we are interested in handling exceptions into a try-block
...
size()] will fail
...
The out_of_range type is defined in the standard library and is in fact used by some
standard-library container access functions
...
See Chapter 13 for further discussion, details, and examples
...
4
...
2 Invariants
The use of exceptions to signal out-of-range access is an example of a function checking its argument and refusing to act because a basic assumption, a precondition, didn’t hold
...
Whenever we define a
function, we should consider what its preconditions are and if feasible test them (see §12
...
4)
...
In particular, we did say ‘‘elem points to
an array of sz doubles’’ but we only said that in a comment
...
It is the job of a constructor
to establish the invariant for its class (so that the member functions can rely on it) and for the member functions to make sure that the invariant holds when they exit
...
It properly initialized the Vector members, but it failed to check
that the arguments passed to it made sense
...
Here is a more appropriate definition:
Vector::Vector(int s)
{
if (s<0) throw length_error{};
elem = new double[s];
sz = s;
}
I use the standard-library exception length_error to report a non-positive number of elements
because some standard-library operations use that exception to report problems of this kind
...
We can now write:
void test()
{
try {
Vector v(−27);
}
catch (std::length_error) {
// handle negative size
}
catch (std::bad_alloc) {
// handle memory exhaustion
}
}
You can define your own classes to be used as exceptions and have them carry arbitrary information
from a point where an error is detected to a point where it can be handled (§13
...
Often, a function has no way of completing its assigned task after an exception is thrown
...
5
...
1)
...
4
...
2
Invariants
57
The notion of invariants is central to the design of classes, and preconditions serve a similar role
in the design of functions
...
The notion of invariants underlies C++’s notions of resource management supported by constructors (§2
...
2) and destructors (§3
...
1
...
2)
...
4, §16
...
1, and §17
...
2
...
3
...
If an error can be found at compile time, it is usually
preferable to do so
...
However, we can also perform simple checks on other properties that are known at compile time and report failures as compiler error messages
...
We call such statements of expectations assertions
...
2
...
4)
...
458;
// km/s
void f(double speed)
{
const double local_max = 160
...
0/(60*60) km/s
static_assert(speed
// error : speed must be a constant
// OK
//
...
The most important uses of static_assert come when we make assertions about types used as
parameters in generic programming (§5
...
2, §24
...
For runtime-checked assertions, see §13
...
2
...
Those are the parts of C++ that underlie all programming techniques and styles supported by C++
...
58
A Tour of C++: The Basics
2
...
1
...
3
...
Focus on programming techniques, not on language features; §2
...
Chapter 2
3
A Tour of C++: Abstraction Mechanisms
Don’t Panic!
– Douglas Adams
•
•
•
•
•
Introduction
Classes
Concrete Types; Abstract Types; Virtual Functions; Class Hierarchies
Copy and Move
Copying Containers; Moving Containers; Resource Management; Suppressing Operations
Templates
Parameterized Types; Function Templates; Function Objects; Variadic Templates; Aliases
Advice
3
...
It informally presents ways of defining and using new types
(user-defined types)
...
Templates are
introduced as a mechanism for parameterizing types and algorithms with (other) types and algorithms
...
These are the language facilities supporting
the programming styles known as object-oriented programming and generic programming
...
The assumption is that you have programmed before
...
Even if you have programmed before, the language you used or the applications you
wrote may be very different from the style of C++ presented here
...
60
A Tour of C++: Abstraction Mechanisms
Chapter 3
As in Chapter 2, this tour presents C++ as an integrated whole, rather than as a layer cake
...
Such historical information can be found in §1
...
3
...
A class is a user-defined type provided to represent a concept in the code of a program
...
, we try to represent it as a class in the program so that the idea is there in the code,
rather than just in our head, in a design document, or in some comments
...
In particular, classes are often what libraries offer
...
By ‘‘better,’’ I mean more correct,
easier to maintain, more efficient, more elegant, easier to use, easier to read, and easier to reason
about
...
The needs and tastes of programmers vary immensely
...
Here, we will just consider the basic support for three important kinds of
classes:
• Concrete classes (§3
...
1)
• Abstract classes (§3
...
2)
• Classes in class hierarchies (§3
...
4)
An astounding number of useful classes turn out to be of these three kinds
...
3
...
1 Concrete Types
The basic idea of concrete classes is that they behave ‘‘just like built-in types
...
Similarly, a vector and a string are much
like built-in arrays, except that they are better behaved (§4
...
3
...
4
...
The defining characteristic of a concrete type is that its representation is part of its definition
...
That allows implementations to be optimally efficient in time and space
...
4
...
g
...
3
...
3)
...
3
...
Therefore, if the representation changes in any significant way, a
user must recompile
...
2
...
For types that don’t change often, and where local variables provide much-needed clarity
and efficiency, this is acceptable and often ideal
...
That’s the way vector and string are implemented; they can
be considered resource handles with carefully crafted interfaces
...
2
...
1 An Arithmetic Type
The ‘‘classical user-defined arithmetic type’’ is complex:
class complex {
double re, im; // representation: two doubles
public:
complex(double r, double i) :re{r}, im{i} {}
complex(double r) :re{r}, im{0} {}
complex() :re{0}, im{0} {}
// construct complex from two scalars
// construct complex from one scalar
// default complex: {0,0}
double real() const { return re; }
void real(double d) { re=d; }
double imag() const { return im; }
void imag(double d) { im=d; }
complex& operator+=(complex z) { re+=z
...
im; return ∗this; }
// add to re and im
// and return the result
complex& operator−=(complex z) { re−=z
...
im; return ∗this; }
complex& operator∗=(complex);
complex& operator/=(complex);
// defined out-of-class somewhere
// defined out-of-class somewhere
};
This is a slightly simplified version of the standard-library complex (§40
...
The class definition
itself contains only the operations requiring access to the representation
...
For practical reasons, it has to be compatible with what Fortran provided 50
years ago, and we need a conventional set of operators
...
This implies that simple operations must be inlined
...
Functions defined in a class are inlined by default
...
A constructor that can be invoked without an argument is called a default constructor
...
By defining a default constructor you eliminate the possibility of uninitialized variables of that type
...
Many useful operations do not require direct access to the representation of complex, so they
can be defined separately from the class definition:
62
A Tour of C++: Abstraction Mechanisms
complex operator+(complex a, complex b) { return a+=b; }
complex operator−(complex a, complex b) { return a−=b; }
complex operator−(complex a) { return {−a
...
imag()}; }
complex operator∗(complex a, complex b) { return a∗=b; }
complex operator/(complex a, complex b) { return a/=b; }
Chapter 3
// unar y minus
Here, I use the fact that an argument passed by value is copied, so that I can modify an argument
without affecting the caller’s copy, and use the result as the return value
...
real()==b
...
imag()==b
...
Class complex can be used like this:
void f(complex z)
{
complex a {2
...
3,0
...
3
complex b {1/a};
complex c {a+z∗complex{1,2
...
if (c != b)
c = −(b/a)+2∗b;
}
The compiler converts operators involving complex numbers into appropriate function calls
...
User-defined operators (‘‘overloaded operators’’) should be used cautiously and conventionally
...
Also, it is not possible to change
the meaning of an operator for built-in types, so you can’t redefine + to subtract ints
...
2
...
2 A Container
A container is an object holding a collection of elements, so we call Vector a container because it is
the type of objects that are containers
...
3
...
4
...
2), provides rangechecked access (§2
...
3
...
However, it
does have a fatal flaw: it allocates elements using new but never deallocates them
...
5), it is not
Section 3
...
1
...
In some environments you can’t use a collector, and sometimes you prefer more precise control of destruction
(§13
...
4) for logical or performance reasons
...
Vector’s constructor allocates some memory on the free store (also
called the heap or dynamic store) using the new operator
...
This is all done without intervention by users of Vector
...
For example:
void fct(int n)
{
Vector v(n);
//
...
{
Vector v2(2∗n);
//
...
} // v2 is destroyed here
//
...
} // v is destroyed here
obeys the same rules for naming, scope, allocation, lifetime, etc
...
For details on how to control the lifetime of an object, see §6
...
This Vector
has been simplified by leaving out error handling; see §2
...
3
...
In particular, it
is the basis for most C++ general resource management techniques (§5
...
3)
...
The destructor deallocates the elements
...
The technique of acquiring resources in a
constructor and releasing them in a destructor, known as Resource Acquisition Is Initialization or
RAII, allows us to eliminate ‘‘naked new operations,’’ that is, to avoid allocations in general code
and keep them buried inside the implementation of well-behaved abstractions
...
Avoiding naked new and naked delete makes code far less
error-prone and far easier to keep free of resource leaks (§5
...
3
...
1
...
We can handle that by creating a Vector with an appropriate number of elements and
then assigning to them, but typically other ways are more elegant
...
• push_back(): Add a new element at the end (at the back of) the sequence
...
void push_back(double);
//
...
For example:
Vector read(istream& is)
{
Vector v;
for (double d; is>>d;)
v
...
Until that happens, each number read is added to the Vector so that at the end, v’s size is the number of elements read
...
The implementation of push_back() is discussed in §13
...
4
...
The way to provide Vector with
a move constructor, so that returning a potentially huge amount of data from read() is cheap, is
explained in §3
...
2
...
2
...
3
Initializing Containers
65
The std::initializer_list used to define the initializer-list constructor is a standard-library type
known to the compiler: when we use a {}-list, such as {1,2,3,4}, the compiler will create an object of
type initializer_list to give to the program
...
23, 3
...
7, 8};
Vector’s
// v1 has 5 elements
// v2 has 4 elements
initializer-list constructor might be defined like this:
Vector::Vector(std::initializer_list
// initialize with a list
:elem{new double[lst
...
size()}
{
copy(lst
...
end(),elem);
// copy from lst into elem
}
3
...
2 Abstract Types
Types such as complex and Vector are called concrete types because their representation is part of
their definition
...
In contrast, an abstract type is a type that
completely insulates a user from implementation details
...
Since we don’t know anything about
the representation of an abstract type (not even its size), we must allocate objects on the free store
(§3
...
1
...
2) and access them through references or pointers (§2
...
5, §7
...
7)
...
2
...
1)
// destructor (§3
...
1
...
The word virtual means ‘‘may be
redefined later in a class derived from this one
...
A class derived from Container provides an implementation for the Container interface
...
Thus, it is not possible to define an object that is just a
Container; a Container can only serve as the interface to a class that implements its operator[]() and
size() functions
...
This Container can be used like this:
void use(Container& c)
{
const int sz = c
...
It
uses size() and [] without any idea of exactly which type provides their implementation
...
3
...
As is common for abstract classes, Container does not have a constructor
...
On the other hand, Container does have a destructor and that destructor
is virtual
...
2
...
A container that implements the functions required by the interface defined by the abstract class
Container could use the concrete class Vector:
class Vector_container : public Container { // Vector_container implements Container
Vector v;
public:
Vector_container(int s) : v(s) { }
// Vector of s elements
˜Vector_container() {}
double& operator[](int i) { return v[i]; }
int size() const { return v
...
’’ Class Vector_container is said to
be derived from class Container, and class Container is said to be a base of class Vector_container
...
The derived class is said to inherit members from its base class, so the use of base and
derived classes is commonly referred to as inheritance
...
3
...
The destructor (˜Vector_container()) overrides the base class destructor
(˜Container())
...
For a function like use(Container&) to use a Container in complete ignorance of implementation
details, some other function will have to make an object on which it can operate
...
For example:
class List_container : public Container { // List_container implements Container
std::list
// (standard-librar y) list of doubles (§4
...
2)
public:
List_container() { }
// empty List
List_container(initializer_list
˜List_container() {}
Section 3
...
2
Abstract Types
67
double& operator[](int i);
int size() const { return ld
...
Usually, I would not implement a container with a subscript operation using a list, because performance of list subscripting is atrocious
compared to vector subscripting
...
A function can create a List_container and have use() use it:
void h()
{
List_container lc = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
use(lc);
}
The point is that use(Container&) has no idea if its argument is a Vector_container, a List_container,
or some other kind of container; it doesn’t need to know
...
It knows
only the interface defined by Container
...
The flip side of this flexibility is that objects must be manipulated through pointers or references
(§3
...
4)
...
2
...
size();
for (int i=0; i!=sz; ++i)
cout << c[i] << '\n';
}
How is the call c[i] in use() resolved to the right operator[]()? When h() calls use(), List_container’s
operator[]() must be called
...
To
achieve this resolution, a Container object must contain information to allow it to select the right
function to call at run time
...
That table is usually
68
A Tour of C++: Abstraction Mechanisms
Chapter 3
called the virtual function table or simply the vtbl
...
This can be represented graphically like this:
vtbl:
Vector_container:
Vector_container::operator[]()
v
Vector_container::size()
Vector_container::˜Vector_container()
List_container:
vtbl:
List_container::operator[]()
ld
List_container::size()
List_container::˜List_container()
The functions in the vtbl allow the object to be used correctly even when the size of the object and
the layout of its data are unknown to the caller
...
This virtual call mechanism can be made almost as efficient as the ‘‘normal function call’’
mechanism (within 25%)
...
3
...
4 Class Hierarchies
The Container example is a very simple example of a class hierarchy
...
g
...
We use class hierarchies to represent concepts that have hierarchical relationships, such as ‘‘A fire engine is a kind of a truck
which is a kind of a vehicle’’ and ‘‘A smiley face is a kind of a circle which is a kind of a shape
...
As a semirealistic classic example, let’s consider shapes on a screen:
Shape
Circle
Triangle
Smiley
The arrows represent inheritance relationships
...
To represent that simple diagram in code, we must first specify a class that defines the general properties of all shapes:
Section 3
...
4
Class Hierarchies
class Shape {
public:
virtual Point center() const =0;
virtual void move(Point to) =0;
69
// pure virtual
virtual void draw() const = 0;
virtual void rotate(int angle) = 0;
// draw on current "Canvas"
virtual ˜Shape() {}
//
...
Given this definition, we can
write general functions manipulating vectors of pointers to shapes:
void rotate_all(vector
{
for (auto p : v)
p−>rotate(angle);
}
To define a particular shape, we must say that it is a
(including its virtual functions):
class Circle : public Shape {
public:
Circle(Point p, int rr);
Shape
and specify its particular properties
// constructor
Point center() const { return x; }
void move(Point to) { x=to; }
void draw() const;
void rotate(int) {}
private:
Point x; // center
int r;
// radius
};
// nice simple algorithm
So far, the
Shape and Circle example provides
Vector_container example, but we can build further:
nothing new compared to the
class Smiley : public Circle { // use the circle as the base for a face
public:
Smiley(Point p, int r) : Circle{p,r}, mouth{nullptr} { }
˜Smiley()
{
delete mouth;
for (auto p : eyes) delete p;
}
Container
and
70
A Tour of C++: Abstraction Mechanisms
Chapter 3
void move(Point to);
void draw() const;
void rotate(int);
void add_eye(Shape∗ s) { eyes
...
private:
vector
Shape∗ mouth;
};
// usually two eyes
The push_back() member function adds its argument to the vector (here, eyes), increasing that
vector’s size by one
...
Shape’s destructor is virtual and Smiley’s destructor overrides it
...
In particular, it may be deleted through a pointer to
a base class
...
That destructor then implicitly invokes the destructors of its bases and members
...
We can add data members, operations, or both as we define a new class by derivation
...
See Chapter
21
...
That is, the base class acts as an interface for the derived class
...
Such classes are often abstract classes
...
Smiley’s uses of Circle’s constructor and of Circle::draw()
are examples
...
Concrete classes – especially classes with small representations – are much like built-in types: we
define them as local variables, access them using their names, copy them around, etc
...
2
...
For example, consider a function that reads data describing
shapes from an input stream and constructs the appropriate Shape objects:
enum class Kind { circle, triangle, smiley };
Shape∗ read_shape(istream& is) // read shape descriptions from input stream is
{
//
...
switch (k) {
case Kind::circle:
// read circle data {Point,int} into p and r
return new Circle{p,r};
case Kind::triangle:
// read triangle data {Point,Point,Point} into p1, p2, and p3
return new Triangle{p1,p2,p3};
case Kind::smiley:
// read smiley data {Point,int,Shape,Shape,Shape} into p, r, e1 ,e2, and m
Smiley∗ ps = new Smiley{p,r};
ps−>add_eye(e1);
ps−>add_eye(e2);
ps−>set_mouth(m);
return ps;
}
}
A program may use that shape reader like this:
void user()
{
std::vector
while (cin)
v
...
The user()
code can be compiled once and later used for new Shapes added to the program
...
This is done
with the delete operator and relies critically on Shape’s virtual destructor
...
This is crucial because a derived
class may have acquired all kinds of resources (such as file handles, locks, and output streams) that
need to be released
...
Experienced programmers will notice that I left open two obvious opportunities for mistakes:
• A user might fail to delete the pointer returned by read_shape()
...
In that sense, functions returning a pointer to an object allocated on the free store are dangerous
...
2
...
// §5
...
1
}
void user()
{
vector
while (cin)
v
...
For the unique_ptr version of user() to work, we need versions of draw_all() and rotate_all() that
accept vector
...
4
...
3
...
This is true for objects of user-defined types as well as for builtin types
...
For example,
using complex from §3
...
1
...
}
// copy initialization
// copy assignment
Now z1, z2, and z3 have the same value because both the assignment and the initialization copied
both members
...
For
simple concrete types, memberwise copy is often exactly the right semantics for copy
...
Section 3
...
1
Copying Containers
73
3
...
1 Copying Containers
When a class is a resource handle, that is, it is responsible for an object accessed through a pointer,
the default memberwise copy is typically a disaster
...
4
...
2)
...
6)
...
Copying of an object of a class is defined by two members: a copy constructor and a copy
assignment:
class Vector {
private:
double∗ elem; // elem points to an array of sz doubles
int sz;
public:
Vector(int s);
// constructor: establish invariant, acquire resources
˜Vector() { delete[] elem; }
// destructor: release resources
Vector(const Vector& a);
Vector& operator=(const Vector& a);
// copy constructor
// copy assignment
double& operator[](int i);
const double& operator[](int i) const;
int size() const;
};
A suitable definition of a copy constructor for Vector allocates the space for the required number of
elements and then copies the elements into it, so that after a copy each Vector has its own copy of
the elements:
74
A Tour of C++: Abstraction Mechanisms
Vector::Vector(const Vector& a)
:elem{new double[sz]},
sz{a
...
elem[i];
}
Chapter 3
// copy constructor
// allocate space for elements
// copy elements
The result of the v2=v1 example can now be presented as:
v1:
v2:
4
2
4
3
Of course, we need a copy assignment in addition to the copy constructor:
Vector& Vector::operator=(const Vector& a)
{
double∗ p = new double[a
...
sz; ++i)
p[i] = a
...
sz;
return ∗this;
}
// copy assignment
The name this is predefined in a member function and points to the object for which the member
function is called
...
3
...
2 Moving Containers
We can control copying by defining a copy constructor and a copy assignment, but copying can be
costly for large containers
...
size()!=b
...
size());
for (int i=0; i!=a
...
3
...
We might use this + like this:
res
75
and into some place
void f(const Vector& x, const Vector& y, const Vector& z)
{
Vector r;
//
...
}
That would be copying a Vector at least twice (one for each use of the + operator)
...
The most embarrassing part is that res in
operator+() is never used again after the copy
...
Fortunately, we can
state that intent:
class Vector {
//
...
This means that r=x+y+z will involve no copying of Vectors
...
As is typical, Vector’s move constructor is trivial to define:
Vector::Vector(Vector&& a)
:elem{a
...
sz}
{
a
...
sz = 0;
}
The && means ‘‘rvalue reference’’ and is a reference to which we can bind an rvalue (§6
...
1)
...
’’ So an rvalue is – to a first approximation – a value
that you can’t assign to, such as an integer returned by a function call, and an rvalue reference is a
reference to something that nobody else can assign to
...
A move constructor does not take a const argument: after all, a move constructor is supposed to
remove the value from its argument
...
A move operation is applied when an rvalue reference is used as an initializer or as the righthand side of an assignment
...
Typically, we should also allow assignment to a moved-from object (§17
...
6
...
Where the programmer knows that a value will not be used again, but the compiler can’t be
expected to be smart enough to figure that out, the programmer can be specific:
Vector f()
{
Vector x(1000);
Vector y(1000);
Vector z(1000);
//
...
return z;
};
// we get a copy
// we get a move
// we get a move
The standard-library function move() returns an rvalue reference to its argument
...
y:
0
1000
1
2
...
3
...
3 Resource Management
By defining constructors, copy operations, move operations, and a destructor, a programmer can
provide complete control of the lifetime of a contained resource (such as the elements of a container)
...
That way, objects that we cannot or would not want to copy out of a scope can be
simply and cheaply moved out instead
...
3
...
We can’t copy the former and don’t want to
copy the latter
...
push_back(move(t));
//
...
// run hear tbeat concurrently (on its own thread)
// move t into my_threads
Section 3
...
3
Resource Management
77
Vector vec(n);
for (int i=0; i
In fact, the standard-library ‘‘smart pointers,’’ such as unique_ptr, are themselves resource
handles (§5
...
1)
...
4
...
In very much the same way as new and delete disappear from application code, we can make
pointers disappear into resource handles
...
In particular, we can achieve strong resource safety; that is, we can
eliminate resource leaks for a general notion of a resource
...
3
...
4 Suppressing Operations
Using the default copy or move for a class in a hierarchy is typically a disaster: given only a pointer
to a base, we simply don’t know what members the derived class has (§3
...
2), so we can’t know
how to copy them
...
};
Now an attempt to copy a Shape will be caught by the compiler
...
2
...
In this particular case, if you forgot to delete a copy or move operation, no harm is done
...
Furthermore, the generation of copy operations is deprecated in this case (§44
...
3)
...
2
...
A base class in a class hierarchy is just one example of an object we wouldn’t want to copy
...
2, §17
...
2)
...
6
...
78
A Tour of C++: Abstraction Mechanisms
Chapter 3
3
...
A vector is a general
concept, independent of the notion of a floating-point number
...
A template is a class or a function that we parameterize with a set of types or values
...
3
...
1 Parameterized Types
We can generalize our vector-of-doubles type to a vector-of-anything type by making it a
and replacing the specific type double with a parameter
...
copy and move operations
...
It is C++’s version of the mathematical ‘‘for all T’’ or more precisely ‘‘for all types T
...
4
...
It is not (as in C++98) necessary to place a space between the two >s
...
size(); ++i)
cout << vs[i] << '\n';
}
// Vector of some strings
To support the range-for loop for our Vector, we must define suitable begin() and end() functions:
template
T∗ begin(Vector
{
return &x[0];
// pointer to first element
}
template
T∗ end(Vector
{
return x
...
size(); // pointer to one-past-last element
}
Given those, we can write:
void f2(const Vector
{
for (auto& s : vs)
cout << s << '\n';
}
Similarly, we can define lists, vectors, maps (that is, associative arrays), etc
...
4,
§23
...
Templates are a compile-time mechanism, so their use incurs no run-time overhead compared to
‘‘handwritten code’’ (§23
...
2)
...
4
...
In
particular, they are extensively used for parameterization of both types and algorithms in the standard library (§4
...
5, §4
...
5)
...
0);
// the sum of a vector of ints (add doubles)
double dd = sum(ld,0
...
0,0
...
Note how the types of the template arguments for sum
arguments
...
This sum() is a simplified version of the standard-library accumulate() (§40
...
3
...
3 Function Objects
One particularly useful kind of template is the function object (sometimes called a functor), which
is used to define objects that can be called like functions
...
We can define named variables of type Less_than for some argument type:
Less_than
// lti(i) will compare i to 42 using < (i<42)
Less_than
We can call such an object, just as we call a function:
void fct(int n, const string & s)
{
bool b1 = lti(n);
// true if n<42
bool b2 = lts(s);
// true if s<"Backus"
//
...
4
...
For example, we can count the
occurrences of values for which a predicate returns true:
template
int count(const C& c, P pred)
{
int cnt = 0;
for (const auto& x : c)
if (pred(x))
++cnt;
return cnt;
}
A predicate is something that we can invoke to return true or false
...
The beauty of these
function objects is that they carry the value to be compared against with them
...
Also, for a simple function object like Less_than inlining is simple,
so that a call of Less_than is far more efficient than an indirect function call
...
Function objects used to specify the meaning of key operations of a general algorithm (such as
Less_than for count()) are often referred to as policy objects
...
That could be seen as inconvenient
...
4)
...
The [&] is a capture list specifying that local names used
(such as x) will be passed by reference
...
Had we wanted to give the generated object a copy of x, we could have said so: [=x]
...
Using lambdas can be convenient and terse, but also obscure
...
In §3
...
4, we noticed the annoyance of having to write many functions to perform operations on
elements of vectors of pointers and unique_ptrs, such as draw_all() and rotate_all()
...
First, we need a function that applies an operation to each object pointed to by the elements of a
container of pointers:
template
void for_all(C& c, Oper op)
// assume that C is a container of pointers
{
for (auto& x : c)
op(∗x);
// pass op() a reference to each element pointed to
}
Now, we can write a version of user() from §3
...
4 without writing a set of _all functions:
void user()
{
vector
while (cin)
v
...
draw(); });
for_all(v,[](Shape& s){ s
...
In particular, those for_all() calls would still work if I changed v
to a vector
...
4
...
Such a
template is called a variadic template
...
Tail>
void f(T head, Tail
...
); // try again with tail
}
void f() { }
// do nothing
The key to implementing a variadic template is to note that when you pass a list of arguments to it,
Section 3
...
4
Variadic Templates
83
you can separate the first argument from the rest
...
The ellipsis,
...
Eventually, of course, tail will become empty and we need a separate
function to deal with that
...
2,"hello");
cout << "\nsecond: "
f(0
...
2,"hello"), which will call f(2
...
What might the call g(head) do? Obviously, in a real program it will do whatever we wanted
done to each argument
...
2 hello
second: 0
...
The strength of variadic templates (sometimes just called variadics) is that they can accept any
arguments you care to give them
...
For details, see §28
...
For examples, see §34
...
4
...
3
...
5 Aliases
Surprisingly often, it is useful to introduce a synonym for a type or a template (§6
...
For example,
the standard header
using size_t = unsigned int;
The actual type named size_t is implementation-dependent, so in another implementation size_t
may be an unsigned long
...
It is very common for a parameterized type to provide an alias for types related to their template
arguments
...
};
In fact, every standard-library container provides value_type as the name of its value type (§31
...
1)
...
For
example:
template
using Element_type = typename C::value_type;
template
void algo(Container& c)
{
Vector
//
...
For example:
template
class Map {
//
...
6
...
5 Advice
[1]
[2]
[3]
[4]
[5]
[6]
[7]
Express ideas directly in code; §3
...
Define classes to represent application concepts directly in code; §3
...
Use concrete classes to represent simple concepts and performance-critical components;
§3
...
1
...
2
...
2
...
2
...
2
...
2
...
Use class hierarchies to represent concepts with inherent hierarchical structure; §3
...
4
...
5
[8]
[9]
[10]
[11]
[12]
[13]
[14]
[15]
Advice
85
When designing a class hierarchy, distinguish between implementation inheritance and interface inheritance; §3
...
4
...
3
...
3
...
Provide strong resource safety; that is, never leak anything that you think of as a resource;
§3
...
3
...
4
...
Use function templates to represent general algorithms; §3
...
2
...
4
...
Use type and template aliases to provide a uniform notation for types that may vary among
similar types or among implementations; §3
...
5
...
1 Libraries
No significant program is written in just a bare programming language
...
These then form the basis for further work
...
Continuing from Chapters 2 and 3, this chapter and the next give a quick tour of key standardlibrary facilities
...
If not, please consider reading a
textbook, such as Programming: Principles and Practice Using C++ [Stroustrup,2009], before
continuing
...
If you find this ‘‘lightning tour’’
confusing, you might skip to the more systematic and bottom-up language presentation starting in
Chapter 6
...
88
A Tour of C++: Containers and Algorithms
Chapter 4
I very briefly present useful standard-library types, such as string, ostream, vector, map (this
chapter), unique_ptr, thread, regex, and complex (Chapter 5), as well as the most common ways of
using them
...
As in Chapter
2 and Chapter 3, you are strongly encouraged not to be distracted or discouraged by an incomplete
understanding of details
...
The specification of the standard library is almost two thirds of the ISO C++ standard
...
Much though have gone into its design, more still into
its implementations, and much effort will go into its maintenance and extension
...
In addition to the standard-library components, most implementations offer ‘‘graphical user
interface’’ systems (GUIs), Web interfaces, database interfaces, etc
...
Here, I do not describe such systems and libraries
...
Naturally, a programmer is encouraged to
explore the more extensive facilities available on most systems
...
1
...
g
...
3
...
• Strings and I/O streams (with support for international character sets and localization); see
Chapter 36, Chapter 38, and Chapter 39
...
• A framework of containers (such as vector and map) and algorithms (such as find(), sort(),
and merge()); see §4
...
5, Chapters 31-33
...
• Support for numerical computation (such as standard mathematical functions, complex
numbers, vectors with arithmetic operations, and random number generators); see §3
...
1
...
• Support for regular expression matching; see §5
...
• Support for concurrent programming, including threads and locks; see §5
...
The concurrency support is foundational so that users can add support for new models of
concurrency as libraries
...
g
...
4
...
2
...
4),
STL-style generic programming (e
...
, pair; §5
...
3, §34
...
4
...
g
...
4
...
2)
...
g
...
2
...
3)
and an interface to garbage collectors (§34
...
• Special-purpose containers, such as array (§34
...
1), bitset (§34
...
2), and tuple (§34
...
4
...
Section 4
...
1
Standard-Library Overview
89
The main criteria for including a class in the library were that:
• it could be helpful to almost every C++ programmer (both novices and experts),
• it could be provided in a general form that did not add significant overhead compared to a
simpler version of the same facility, and
• that simple uses should be easy to learn (relative to the inherent complexity of their task)
...
4
...
2 The Standard-library Headers and Namespace
Every standard-library facility is provided through some standard header
...
The standard library is defined in a namespace (§2
...
2, §14
...
1) called
library facilities, the std:: prefix can be used:
std
...
Neither will I always
#include the necessary headers explicitly
...
4
...
5
...
2) and make the names
they declare accessible
...
However, in this book, I use the standard library almost exclusively and it is good to know what it
offers
...
Nor do I #include the
appropriate headers in every example
...
Here is a selection of standard-library headers, all supplying declarations in namespace std:
Selected Standard Library Headers (continues)
copy(), find(), sort()
array
duration, time_point
sqrt(), pow()
complex, sqrt(), pow()
fstream, ifstream, ofstream
future, promise
istream, ostream, cin, cout
§32
...
2
...
2
§40
...
4
§38
...
1
§5
...
5
§38
...
25
§iso
...
3
...
20
...
2
§iso
...
8
§iso
...
8
§iso
...
9
...
30
...
27
...
4
...
2
...
7
Chapter 37
Chapter 36
§31
...
3
§38
...
2
§5
...
1
§31
...
3
...
5
§31
...
23
...
4
§iso
...
6
§iso
...
5
§iso
...
8
§iso
...
3
§iso
...
4
...
27
...
30
...
23
...
4
§iso
...
1
§iso
...
3
...
2 for more information
...
2 Strings
The standard library provides a string type to complement the string literals
...
For example:
string
type pro-
string compose(const string& name, const string& domain)
{
return name + '@' + domain;
}
auto addr = compose("dmr","bell−labs
...
com
...
You can concatenate a string, a string literal, a C-style string, or a character to a
string
...
3
...
In many applications, the most common form of concatenation is adding something to the end
of a string
...
For example:
void m2(string& s1, string& s2)
{
s1 = s1 + '\n'; // append newline
s2 += '\n';
// append newline
}
The two ways of adding to the end of a string are semantically equivalent, but I prefer the latter
because it is more explicit about what it does, more concise, and possibly more efficient
...
In addition to = and +=, subscripting (using []) and substring operations are
supported
...
Among other useful features, it
provides the ability to manipulate substrings
...
2
Strings
91
string name = "Niels Stroustrup";
void m3()
{
string s = name
...
replace(0,5,"nicholas");
name[0] = toupper(name[0]);
}
// s = "Stroustrup"
// name becomes "nicholas Stroustrup"
// name becomes "Nicholas Stroustrup"
The substr() operation returns a string that is a copy of the substring indicated by its arguments
...
Since indexing starts from 0, s gets the value Stroustrup
...
In this case, the substring starting at 0
with length 5 is Niels; it is replaced by nicholas
...
Thus, the final value of name is Nicholas Stroustrup
...
Naturally, strings can be compared against each other and against string literals
...
}
//
...
The most common techniques for implementing
example (§19
...
4
...
The input operations are typed and extensible to handle user-defined types
...
Other forms of user interaction, such as graphical I/O, are handled through libraries that are not
part of the ISO standard and therefore not described here
...
3
...
Further, it is easy to define output of a
user-defined type (§4
...
3)
...
By default, values written to cout are converted to a sequence of characters
...
Equivalently, we could write:
void g()
{
int i {10};
cout << i;
}
Output of different types can be combined in the obvious way:
void h(int i)
{
cout << "the value of i is ";
cout << i;
cout << '\n';
}
For h(10), the output will be:
the value of i is 10
People soon tire of repeating the name of the output stream when outputting several related items
...
For example:
void h2(int i)
{
cout << "the value of i is " << i << '\n';
}
This h2() produces the same output as h()
...
Note that a character is output as
a character rather than as a numerical value
...
Section 4
...
2
Input
93
4
...
2 Input
The standard library offers istreams for input
...
The operator >> (‘‘get from’’) is used as an input operator; cin is the standard input stream
...
For example:
void f()
{
int i;
cin >> i;
double d;
cin >> d;
// read an integer into i
// read a double-precision floating-point number into d
}
This reads a number, such as 1234, from the standard input into the integer variable i and a floatingpoint number, such as 12
...
Often, we want to read a sequence of characters
...
For example:
void hello()
{
cout << "Please enter your name\n";
string str;
cin >> str;
cout << "Hello, " << str << "!\n";
}
If you type in Eric the response is:
Hello, Eric!
By default, a whitespace character (§7
...
2), such as a space, terminates the read, so if you enter Eric
pretending to be the ill-fated king of York, the response is still:
Bloodaxe
Hello, Eric!
You can read a whole line (including the terminating newline character) using the getline() function
...
The standard strings have the nice property of expanding to hold what you put in them; you
don’t have to precalculate a maximum size
...
4
...
3 I/O of User-Defined Types
In addition to the I/O of built-in types and standard strings, the iostream library allows programmers
to define I/O for their own types
...
name << "\", " << e
...
See §38
...
2 for details
...
Note: formatted with { " " , and }
{
char c, c2;
if (is>>c && c=='{' && is>>c2 && c2=='"') { // star t with a { "
string name;
// the default value of a string is the empty string: ""
while (is
...
setf(ios_base::failbit);
return is;
// register the failure in the stream
}
An input operation returns a reference to its
istream
which can be used to test if the operation
Section 4
...
3
I/O of User-Defined Types
95
succeeded
...
get(c) does not, so that this Entry-input operator
ignores (skips) whitespace outside the name string, but not within it
...
4
...
See §5
...
4
...
Reading characters into a string and printing out the string is a simple example
...
Providing suitable containers for
a given task and supporting them with useful fundamental operations are important steps in the
construction of any program
...
This is the kind of program for which different approaches appear ‘‘simple and
obvious’’ to people of different backgrounds
...
3
...
Here, we deliberately ignore many real-world complexities, such as the
fact that many phone numbers do not have a simple representation as a 32-bit int
...
4
...
A vector is a sequence of elements of a given
type
...
2
...
4 give an idea of the implementation of vector and §13
...
4 provide an exhaustive discussion
...
size(); ++i)
cout << book[i] << '\n';
}
As usual, indexing starts at 0 so that book[0] holds the entry for David Hume
...
The elements of a vector constitute a range, so we can use a range-for loop (§2
...
5):
void print_book(const vector
{
for (const auto& x : book)
// for "auto" see §2
...
2
cout << x << '\n';
}
When we define a vector, we give it an initial size (initial number of elements):
vector
vector
vector
vector
...
9
An explicit size is enclosed in ordinary parentheses, for example, (23), and by default the elements
are initialized to the element type’s default value (e
...
, nullptr for pointers and 0 for numbers)
...
g
...
9 for the 32 elements of v4)
...
One of the most useful operations on a vector is push_back(),
which adds a new element at the end of a vector, increasing its size by one
...
push_back(e);
}
This reads Entrys from the standard input into phone_book until either the end-of-input (e
...
, the
end of a file) is reached or the input operation encounters a format error
...
A vector can be copied in assignments and initializations
...
4
...
3
...
Thus, after the initialization
of book2, book2 and phone_book hold separate copies of every Entry in the phone book
...
Where copying is undesirable, references or pointers (§7
...
7) or move operations (§3
...
2,
§17
...
2) should be used
...
4
...
1 Elements
Like all standard-library containers, vector is a container of elements of some type T, that is, a
vector
...
When you insert a new element, its value is copied
into the container
...
The element is not a reference or a pointer to some object
containing 7
...
For people who care about
memory sizes and run-time performance this is critical
...
4
...
2 Range Checking
The standard-library vector does not guarantee range checking (§31
...
2)
...
size()]
...
}
// book
...
This is
undesirable, and out-of-range errors are a common problem
...
3
...
1
T& operator[](int i)
{ return vector
const T& operator[](int i) const
{ return vector
// range check
// range check const objects; §3
...
1
...
The at() operation is a vector subscript operation that throws an exception of type
out_of_range if its argument is out of the vector’s range (§2
...
3
...
2
...
Vec
98
A Tour of C++: Containers and Algorithms
Chapter 4
For Vec, an out-of-range access will throw an exception that the user can catch
...
size()] = {"Joe",999999};
//
...
4
...
1, Chapter 13)
...
One way to minimize surprises from uncaught exceptions is to use a main()
with a try-block as its body
...
) {
cerr << "unknown exception thrown\n";
}
This provides default exception handlers so that if we fail to catch some exception, an error message is printed on the standard error-diagnostic output stream cerr (§38
...
Some implementations save you the bother of defining Vec (or equivalent) by providing a rangechecked version of vector (e
...
, as a compiler option)
...
4
...
Insertion and deletion of phone book entries could be common, so a list could be appropriate for representing a simple phone book
...
4
...
Instead, we might search the list looking for an element with a given value
...
5:
int get_number(const string& s)
{
for (const auto& x : phone_book)
if (x
...
number;
return 0; // use 0 to represent "number not found"
}
The search for s starts at the beginning of the list and proceeds until s is found or the end of
phone_book is reached
...
For example, we may want to delete it or
insert a new entry before it
...
Every standard-library container provides the functions begin() and end(), which return an iterator to the first and to one-past-the-last
element, respectively (§4
...
1
...
Using iterators explicitly, we can – less elegantly – write the
get_number() function like this:
int get_number(const string& s)
{
for (auto p = phone_book
...
end(); ++p)
if (p−>name==s)
return p−>number;
return 0; // use 0 to represent "number not found"
}
In fact, this is roughly the way the terser and less error-prone range-for loop is implemented by the
compiler
...
m
...
insert(p,ee);
// add ee before the element referred to by p
phone_book
...
3
...
These list examples could be written identically using vector and (surprisingly, unless you
understand machine architecture) perform better with a small vector than with a small list
...
Unless
you have a reason not to, use a vector
...
g
...
g
...
100
A Tour of C++: Containers and Algorithms
Chapter 4
4
...
3 map
Writing code to look up a name in a list of (name,number) pairs is quite tedious
...
The standard library offers a search tree (a redblack tree) called map:
map:
links
4
links
key:
value:
links
links
In other contexts, a map is known as an associative array or a dictionary
...
The standard-library map (§31
...
3) is a container of pairs of values optimized for lookup
...
4
...
4
...
For example:
int get_number(const string& s)
{
return phone_book[s];
}
In other words, subscripting a map is essentially the lookup we called get_number()
...
The default value for an integer
type is 0; the value I just happened to choose represents an invalid telephone number
...
4
...
1)
...
4
...
That’s pretty
good
...
However, in many cases, we can do better by using a hashed
lookup rather than comparison using an ordering function, such as <
...
4
...
4
...
4)
...
If necessary, you
4
...
5 Container Overview
The standard library provides some of the most general and useful container types to allow the programmer to select a container that best serves the needs of an application:
Standard Container Summary
vector
list
forward_list
deque
set
multiset
map
multimap
unordered_map
unordered_multimap
unordered_set
unordered_multiset
A variable-size vector (§31
...
4
...
4
...
2)
A set (§31
...
3)
A set in which a value can occur many times (§31
...
3)
An associative array (§31
...
3)
A map in which a key can occur many times (§31
...
3)
A map using a hashed lookup (§31
...
3
...
4
...
2)
A set using a hashed lookup (§31
...
3
...
4
...
2)
The unordered containers are optimized for lookup with a key (often a string); in other words, they
are implemented using hash tables
...
4
...
(§4
...
2, §30
...
In addition, the standard
library provides container adaptors queue
...
2), stack
...
1), deque
...
5
...
The standard library also provides more specialized container-like
types, such as a fixed-size array array
...
1) and bitset
...
2)
...
Furthermore, the meanings of the operations are equivalent for the various containers
...
For example:
• begin() and end() give iterators to the first and one-beyond-the-last elements, respectively
...
• size() returns the number of elements
...
The range-checked vector, Vector
(§2
...
2, §2
...
3
...
The uniformity of container interfaces also allows us to
specify algorithms independently of individual container types
...
For example, subscripting and traversing a vector is cheap and easy
...
Please note that a vector is usually more efficient than a list for short sequences of small
elements (even for insert() and erase())
...
4
...
To use one, we need operations for basic access such as adding and removing elements (as is provided for list and vector)
...
We sort them, print them, extract subsets,
remove elements, search for objects, etc
...
For
example, the following sorts a vector and places a copy of each unique vector element on a list:
bool operator<(const Entry& x, const Entry& y)
// less than
{
return x
...
name;
// order Entrys by their names
}
void f(vector
{
sort(vec
...
end());
unique_copy(vec
...
end(),lst
...
They are expressed in terms of sequences of
elements
...
5
Algorithms
iterators:
begin()
103
end()
elements:
In the example, sort() sorts the sequence defined by the pair of iterators vec
...
end() –
which just happens to be all the elements of a vector
...
If more than one element is written, the elements following that initial element will be overwritten
...
If we wanted to place the unique elements in a new container, we could have written:
list
{
list
sort(vec
...
end());
unique_copy(vec
...
end(),back_inserter(res)); // append to res
return res;
}
A back_inserter() adds elements at the end of a container, extending the container to make room for
them (§33
...
2)
...
5
...
The standard-library list has
a move constructor (§3
...
2, §17
...
2) that makes returning res by value efficient (even for lists of
thousands of elements)
...
begin(),vec
...
5
...
4
...
1 Use of Iterators
When you first encounter a container, a few iterators referring to useful elements can be obtained;
begin() and end() are the best examples of this
...
For
example, the standard algorithm find looks for a value in a sequence and returns an iterator to the
element found:
bool has_c(const string& s, char c)
{
auto p = find(s
...
end(),c);
if (p!=s
...
’’ An equivalent, shorter, definition of has_c() is:
104
A Tour of C++: Containers and Algorithms
Chapter 4
bool has_c(const string& s, char c)
// does s contain the character c?
{
return find(s
...
end(),c)!=s
...
We can return the set of occurrences as a vector of string iterators
...
3
...
Assuming that we would like to
modify the locations found, we pass a non-const string:
vector
{
vector
for (auto p = s
...
end(); ++p)
if (∗p==c)
res
...
We could test
find_all() like this:
void test()
{
string m {"Mary had a little lamb"};
for (auto p : find_all(m,'a'))
if (∗p!='a')
cerr << "a bug!\n";
}
That call of find_all() could be graphically represented like this:
find_all(m,’a’):
m:
M a
r
y
h a d
a
l
i
t
t
l
e
l
a m b
Iterators and standard algorithms work equivalently on every standard container for which their use
makes sense
...
begin(); p!=c
...
push_back(p);
return res;
}
// find all occurrences of v in c
Section 4
...
1
Use of Iterators
105
The typename is needed to inform the compiler that C’s iterator is supposed to be a type and not a
value of some type, say, the integer 7
...
4
...
begin(); p!=c
...
push_back(p);
return res;
}
We can now write:
void test()
{
string m {"Mary had a little lamb"};
for (auto p : find_all(m,'a'))
if (∗p!='a')
cerr << "string bug!\n";
// p is a string::iterator
list
...
2, 3
...
1};
for (auto p : find_all(ld,1
...
1)
cerr << "list bug!\n";
vector
for (auto p : find_all(vs,"green"))
if (∗p!="green")
cerr << "vector bug!\n";
for (auto p : find_all(vs,"green"))
∗p = "vert";
}
Iterators are used to separate algorithms and containers
...
Conversely, a
container knows nothing about the algorithms operating on its elements; all it does is to supply iterators upon request (e
...
, begin() and end())
...
4
...
2 Iterator Types
What are iterators really? Any particular iterator is an object of some type
...
These iterator types can be as different as the containers and
the specialized needs they serve
...
A list iterator must be something more complicated than a simple pointer to an element because
an element of a list in general does not know where the next element of that list is
...
What is common for all iterators is their semantics and the naming of their operations
...
Similarly, ∗ yields
the element to which the iterator refers
...
1
...
Furthermore, users rarely need to know the type of a specific iterator; each
container ‘‘knows’’ its iterator types and makes them available under the conventional names iterator and const_iterator
...
We rarely have to worry about the details of how that type is defined
...
5
...
However, containers are not the only place where we find sequences of elements
...
Consequently, the notion of iterators can be usefully applied to input and output
...
For example:
Section 4
...
3
Stream Iterators
ostream_iterator
107
// write strings to cout
The effect of assigning to ∗oo is to write the assigned value to cout
...
The ++oo is done to
mimic writing into an array through a pointer
...
Again, we must specify the stream to be used and the type of values expected:
istream_iterator
Input iterators are used in pairs representing a sequence, so we must provide an
indicate the end of input
...
Instead, they are provided as
arguments to algorithms
...
begin(),b
...
begin(),b
...
eof() || !os;
// return error state (§2
...
1, §38
...
The ostream_iterator’s second argument is used to delimit output values
...
We read the strings into a vector, then we
sort() them, and then we write them out, eliminating duplicates
...
This can be done by keeping the strings in a set, which does not keep duplicates and keeps its elements in order (§31
...
3)
...
begin(),b
...
begin(),b
...
eof() || !os;
// return error state (§2
...
1, §38
...
4
...
4 Predicates
In the examples above, the algorithms have simply ‘‘built in’’ the action to be done for each element of a sequence
...
For
example, the find algorithm (§32
...
A
more general variant looks for an element that fulfills a specified requirement, a predicate (§3
...
2)
...
A map allows us to
access its elements as a sequence of (key,value) pairs, so we can search a map
for a pair
void f(map
{
auto p = find_if(m
...
end(),Greater_than{42});
//
...
4
...
second>val; }
};
Alternatively, we could use a lambda expression (§3
...
3):
int cxx = count_if(m
...
end(), [](const pair
...
5
...
5
...
Definiteness
...
Output
...
1]
...
The standard library provides dozens of algorithms
...
These standard-library algorithms all take sequences
as inputs (§4
...
A half-open sequence from b to e is referred to as [b:e)
...
4
...
6 Container Algorithms
A sequence is defined by a pair of iterators [begin:end)
...
For example:
sort(v
...
end());
Why don’t we just say sort(v)? We can easily provide that shorthand:
namespace Estd {
using namespace std;
template
void sort(C& c)
{
sort(c
...
end());
}
110
A Tour of C++: Containers and Algorithms
Chapter 4
template
void sort(C& c, Pred p)
{
sort(c
...
end(),p);
}
//
...
4
...
1
...
1
...
1
...
1
...
Remember that standard-library facilities are defined in namespace std; §4
...
2
...
2
...
2, §4
...
2
...
3
...
4
...
4
...
4
...
Prefer compact data structures; §4
...
1
...
If in doubt, use a range-checked vector (such as Vec); §4
...
1
...
Use push_back() or back_inserter() to add elements to a container; §4
...
1, §4
...
Use push_back() on a vector rather than realloc() on an array; §4
...
Catch common exceptions in main(); §4
...
1
...
Know your standard algorithms and prefer them over handwritten loops; §4
...
5
...
5
...
Estd
5
A Tour of C++: Concurrency and Utilities
When you wish to instruct,
be brief
...
1 Introduction
From an end-user’s perspective, the ideal standard library would provide components directly supporting essentially every need
...
However, that is not what the C++ standard library is trying to do
...
Instead, the C++
standard library aims to provide components that are useful to most people in most application
areas
...
In addition, support for a few widely important application areas, such as mathematical computation and text
manipulation, have crept in
...
2 Resource Management
One of the key tasks of any nontrivial program is to manage resources
...
Examples are memory, locks,
sockets, thread handles, and file handles
...
Even for short programs, a leak can become an embarrassment, say by a resource
shortage increasing the run time by orders of magnitude
...
To do this, they rely on the
basic language support for resource management using constructor/destructor pairs to ensure that a
resource doesn’t outlive an object responsible for it
...
2
...
2) and all standard-library containers are implemented in similar ways
...
For example, the technique is used for the standard-library lock classes:
mutex m; // used to protect access to shared data
//
...
manipulate shared data
...
3
...
The corresponding destructor releases the resource
...
This is an application of the ‘‘Resource Acquisition Is Initialization’’ technique (RAII; §3
...
1
...
3)
...
Containers
(such as vector and map), string, and iostream manage their resources (such as file handles and buffers) similarly
...
2
...
3
...
3
...
For example:
void f(int i, int j)
// X* vs
...
Section 5
...
1
unique_ptr
if (i<99) throw Z{};
if (j<77) return;
p−>do_something();
sp−>do_something();
//
...
On the other hand, unique_ptr ensures that its object
is properly destroyed whichever way we exit f() (by throwing an exception, by executing return, or
by ‘‘falling off the end’’)
...
}
// use a local variable
Unfortunately, overuse of new (and of pointers and references) seems to be an increasing problem
...
Its further
uses include passing free-store allocated objects in and out of functions:
unique_ptr
// make an X and immediately give it to a unique_ptr
{
//
...
return unique_ptr
}
A unique_ptr is a handle to an individual object (or an array) in much the same way that a vector is
a handle to a sequence of objects
...
The shared_ptr is similar to unique_ptr except that shared_ptrs are copied rather than moved
...
For example:
void f(shared_ptr
void g(shared_ptr
void user(const string& name, ios_base::openmode mode)
{
shared_ptr
if (!∗fp) throw No_file{}; // make sure the file was properly opened
f(fp);
g(fp);
//
...
Note that f() or g() may spawn a task holding a copy of fp or in some
other way store a copy that outlives user()
...
This is
neither cost free nor exorbitantly expensive, but does make the lifetime of the shared object hard to
predict
...
Given unique_ptr and shared_ptr, we can implement a complete ‘‘no naked new’’ policy
(§3
...
1
...
However, these ‘‘smart pointers’’ are still conceptually pointers and
therefore only my second choice for resource management – after containers and other types that
manage their resources at a higher conceptual level
...
Data races
(§41
...
4) and other forms of confusion are not addressed simply by eliminating the resource management issues
...
’’
• When we share an object, we need pointers (or references) to refer to the shared object, so a
shared_ptr becomes the obvious choice (unless there is an obvious single owner)
...
• A shared polymorphic object typically requires shared_ptrs
...
3
...
5
...
All modern programming languages provide support for this
...
The standard-library support is primarily aimed at supporting systems-level concurrency rather than directly providing sophisticated higher-level concurrency models; those can be supplied as libraries built using the standard-library facilities
...
To allow that, C++ provides a suitable memory model (§41
...
3)
...
This section briefly gives examples of the main standard-library
concurrency support facilities: threads, mutexes, lock() operations, packaged_tasks, and futures
...
Section 5
...
1
Tasks and threads
115
5
...
1 Tasks and threads
We call a computation that can potentially be executed concurrently with other computations a task
...
A task to be executed concurrently with other tasks is launched by constructing a std::thread (found in
its argument
...
4
...
join();
t2
...
To ‘‘join’’ means to
‘‘wait for the thread to terminate
...
In this, threads differ from processes, which
generally do not directly share data
...
3
...
Such communication is typically controlled by locks or other
mechanisms to prevent data races (uncontrolled concurrent access to a variable)
...
Consider possible implementations of the
tasks f (a function) and F (a function object):
void f() { cout << "Hello "; }
struct F {
void operator()() { cout << "Parallel World!\n"; }
};
This is an example of a bad error: Here, f and F() each use the object cout without any form of synchronization
...
The program may produce ‘‘odd’’ output, such as
PaHerallllel o World!
When defining tasks of a concurrent program, our aim is to keep tasks completely separate except
where they communicate in simple and obvious ways
...
For that to work, we just
have to pass arguments, get a result back, and make sure that there is no use of shared data in
between (no data races)
...
3
...
We can easily pass data (or pointers or references to the
data) as arguments
...
4
...
join();
t2
...
F can now use that array and
hopefully no other task accesses vec2 while F is executing
...
The initialization with {f,some_vec} uses a thread variadic template constructor that can accept
an arbitrary sequence of arguments (§28
...
The compiler checks that the first argument can be
invoked given the following arguments and builds the necessary function object to pass to the
thread
...
5
...
3 Returning Results
In the example in §5
...
2, I pass the arguments by non-const reference
...
7)
...
A less obscure technique is to pass the input data by const reference and to pass the location of a place to deposit the result as a separate argument:
void f(const vector
class F {
public:
F(const vector
void operator()();
// place result in *res
Section 5
...
3
private:
const vector
double∗ res;
};
Returning Results
117
// source of input
// target for output
int main()
{
vector
vector
//
...
join();
t2
...
3
...
1
...
3
...
In that case, the access has to be synchronized so that at most
one task at a time has access
...
g
...
The fundamental element of the solution is a mutex, a ‘‘mutual exclusion object
...
lock())
...
Once a thread has completed its access to the shared data, the unique_lock releases the mutex (with
a call m
...
The mutual exclusion and locking facilities are found in
...
Obviously, this is error-prone,
and equally obviously we try to make the correspondence clear through various language means
...
};
It doesn’t take a genius to guess that for a Record called rec, rec
...
It is not uncommon to need to simultaneously access several resources to perform some action
...
For example, if thread1 acquires mutex1 and then tries to acquire mutex2
while thread2 acquires mutex2 and then tries to acquire mutex1, then neither task will ever proceed
further
...
unique_lock
unique_lock
unique_lock
//
...
manipulate shared data
...
The destructors for the individual unique_locks ensure that the
mutexes are released when a thread leaves the scope
...
In particular, the programmer has to
devise ways of knowing what work has and has not been done by various tasks
...
On the other hand, some people are convinced that sharing must be more efficient than copying arguments and returns
...
On the other hand, modern machines are very good at copying data, especially compact
data, such as vector elements
...
5
...
4
...
The simplest ‘‘event’’ is simply time passing
...
3
...
1
Waiting for Events
using namespace std::chrono;
119
// see §35
...
count() << " nanoseconds passed\n";
Note that I didn’t even have to launch a thread; by default, this_thread refers to the one and only
thread (§42
...
6)
...
See §5
...
1 and
§35
...
The time facilities are found in
...
3
...
A condition_variable is a mechanism allowing one thread to
wait for another
...
Consider the classical example of two threads communicating by passing messages through a
queue
...
};
// object to be communicated
queue
condition_variable mcond;
mutex mmutex;
// the queue of messages
// the variable communicating events
// the locking mechanism
The types queue, condition_variable, and mutex are provided by the standard library
...
wait(lck)) /* do nothing */;
auto m = mqueue
...
pop();
lck
...
process m
...
Waiting on condition_variable releases its lock argument until the wait is
over (so that the queue is non-empty) and then reacquires it
...
fill the message
...
push(m);
mcond
...
3
...
5
...
5 Communicating Tasks
The standard library provides a few facilities to allow programmers to operate at the conceptual
level of tasks (work to potentially be done concurrently) rather than directly at the lower level of
threads and locks:
[1] future and promise for returning a value from a task spawned on a separate thread
[2] packaged_task to help launch tasks and connect up the mechanisms for returning a result
[3] async() for launching of a task in a manner very similar to calling a function
...
5
...
5
...
The basic
idea is simple: When a task wants to pass a value to another, it puts the value into a promise
...
We can represent this graphically:
task1:
task2:
set_value()
get()
future
promise
set_exception()
value
If we have a future
X v = fx
...
If the value couldn’t be computed,
get() might throw an exception (from the system or transmitted from the task from which we were
trying to get() the value)
...
3
...
1
future
and promise
121
The main purpose of a promise is to provide simple ‘‘put’’ operations (called set_value() and
set_exception()) to match future’s get()
...
They are yet another fertile source of puns
...
For example:
void f(promise
{
//
...
compute a value for res
...
set_value(res);
}
catch (
...
set_exception(current_exception());
}
}
The current_exception() refers to the caught exception (§30
...
1
...
To deal with an exception transmitted through a future, the caller of
catch it somewhere
...
try {
X v = fx
...
use v
...
) {
// oops: someone couldn’t compute v
//
...
}
}
5
...
5
...
A packaged_task provides wrapper
code to put the return value or exception from the task into a promise (like the code shown in
§5
...
5
...
If you ask it by calling get_future, a packaged_task will give you the future corresponding
to its promise
...
4
...
6):
122
A Tour of C++: Concurrency and Utilities
Chapter 5
double accum(double∗ beg, double ∗ end, double init)
// compute the sum of [beg:end) starting with the initial value init
{
return accumulate(beg,end,init);
}
double comp2(vector
{
using Task_type = double(double∗,double∗,double);
// type of task
packaged_task
packaged_task
// package the task (i
...
, accum)
future
...
get_future()};
// get hold of pt0’s future
// get hold of pt1’s future
double∗ first = &v[0];
thread t1 {move(pt0),first,first+v
...
size()/2,first+v
...
return f0
...
get();
// get the results
}
The packaged_task template takes the type of the task as its template argument (here Task_type, an
alias for double(double∗,double∗,double)) and the task as its constructor argument (here, accum)
...
Please note the absence of explicit mention of locks in this code: we are able to concentrate on
tasks to be done, rather than on the mechanisms used to manage their communication
...
5
...
5
...
It is far from the only model supported by the C++ standard library, but it serves well for a
wide range of needs
...
g
...
To launch tasks to potentially run asynchronously, we can use async():
double comp4(vector
// spawn many tasks if v is large enough
{
if (v
...
begin(),v
...
0);
auto v0 = &v[0];
auto sz = v
...
3
...
3
async()
auto f0 = async(accum,v0,v0+sz/4,0
...
0);
auto f2 = async(accum,v0+sz/2,v0+sz∗3/4,0
...
0);
123
// first quarter
// second quarter
// third quarter
// four th quar ter
return f0
...
get()+f2
...
get(); // collect and combine the results
}
Basically, async() separates the ‘‘call part’’ of a function call from the ‘‘get the result part,’’ and separates both from the actual execution of the task
...
Instead, you think just in terms of tasks that potentially compute their results
asynchronously
...
For example, async() may check whether any idle cores (processors) are available before deciding how many threads to use
...
For example, it can also be used to spawn a task for getting information
from a user, leaving the ‘‘main program’’ active with something else (§42
...
6)
...
4 Small Utility Components
Not all standard-library components come as part of obviously labeled facilities, such as ‘‘containers’’ or ‘‘I/O
...
• Type functions, such as iterator_traits and is_arithmetic, for gaining information about types
...
The point here is that a function or a type need not be complicated or closely tied to a mass of other
functions and types to be useful
...
5
...
1 Time
The standard library provides facilities for dealing with time
...
2
auto t0 = high_resolution_clock::now();
do_work();
auto t1 = high_resolution_clock::now();
cout << duration_cast
...
Subtracting two time_points gives a duration (a
period of time)
...
That’s what duration_cast does
...
2)
...
Guesses about performance are most unreliable
...
4
...
The standard library provides a variety of type functions to help library implementers and programmers in general to write code that take advantage of aspects of the language,
the standard library, and code in general
...
6
...
For example:
constexpr float min = numeric_limits<float>::min();
// smallest positive float (§40
...
2
...
For example:
constexpr int szi = sizeof(int); // the number of bytes in an int
Such type functions are part of C++’s mechanisms for compile-time computation that allow tighter
type checking and better performance than would otherwise have been possible
...
Here, I just present two facilities provided by the standard library: iterator_traits
(§5
...
2
...
4
...
2)
...
4
...
1 iterator_traits
The standard-library sort() takes a pair of iterators supposed to define a sequence (§4
...
Furthermore, those iterators must offer random access to that sequence, that is, they must be randomaccess iterators
...
In particular, a forward_list is a singly-linked list so subscripting would be expensive and there is no reasonable way
to refer back to a previous element
...
1
...
The standard library provides a mechanism, iterator_traits that allows us to check which kind of
iterator is supported
...
5
...
For example:
void test(vector
{
sort(v); // sor t the vector
sort(lst); // sor t the singly-linked list
}
The techniques needed to make that work are generally useful
...
The version taking random-access iterator
arguments is trivial:
Section 5
...
2
...
begin(),v
...
begin(),v
...
3
...
3)
...
The real ‘‘type magic’’ is in the selection of helper functions:
template
void sort(C& c)
{
using Iter = Iterator_type
sort_helper(c
...
end(),Iterator_category
}
Here, I use two type functions: Iterator_type
then Iterator_category
• std::random_access_iterator_tag if C’s iterator supports random access
...
Given that, we can select between the two sorting algorithms at compile time
...
The standard-library support for techniques for using iterators, such as tag dispatch, comes in
the form of a simple class template iterator_traits from
...
3)
...
But then you can’t use the
techniques they support to improve your own code
...
4
...
2 Type Predicates
A standard-library type predicate is a simple type function that answers a fundamental question
about types
...
4
...
Other examples are is_class,
is_pod, is_literal_type, has_virtual_destructor, and is_base_of
...
For example:
template
class complex {
Scalar re, im;
public:
static_assert(Is_arithmetic
//
...
5
...
3 pair and tuple
Often, we need some data that is just data; that is, a collection of values, rather than an object of a
class with a well-defined semantics and an invariant for its value (§2
...
3
...
4)
...
Alternatively, we could let the standard library write the definition for us
...
6
...
We can use that to search in a sorted sequence of Records:
auto rec_eq = [](const Record& r1, const Record& r2) { return r1
...
name;};// compare names
void f(const vector
// assume that v is sorted on its "name" field
{
auto er = equal_range(v
...
end(),Record{"Reg"},rec_eq);
Section 5
...
3
pair
for (auto p = er
...
second; ++p)
cout << ∗p;
and tuple
127
// print all equal records
// assume that << is defined for Record
}
The first member of a pair is called first and the second member is called second
...
The standard-library pair (from
elsewhere
...
The make_pair() function makes it easy to create a pair without explicitly mentioning its type (§34
...
4
...
For example:
void f(vector
{
auto pp = make_pair(v
...
}
// pp is a pair
If you need more than two elements (or less), you can use tuple (from
...
4
...
A tuple
is a heterogeneous sequence of elements; for example:
tuple
...
23);
// the type is deduced
// t is a tuple
string s = get<0>(t); // get first element of tuple
int x = get<1>(t);
double d = get<2>(t);
The elements of a tuple are numbered (starting with zero), rather than named the way elements of
pairs are (first and second)
...
5
...
Like pairs, tuples can be assigned and compared if their elements can be
...
It is less common to need three or more parts to
a result, so tuples are more often found in the implementations of generic algorithms
...
5 Regular Expressions
Regular expressions are a powerful tool for text processing
...
g
...
S
...
In
support for regular expressions in the form of the std::regex class and its supporting functions
...
It specifies a pattern starting with two letters \w{2} optionally followed by some space \s∗
followed by five digits \d{5} and optionally followed by a dash and four digits −\d{4}
...
Regular expressions are summarized in §37
...
1
...
3
...
1) starting with R"( and terminated by )"
...
The simplest way of using a pattern is to search for it in a stream:
int lineno = 0;
for (string line; getline(cin,line);) {
// read into line buffer
++lineno;
smatch matches;
// matched strings go here
if (regex_search(line,matches,pat))
// search for pat in line
cout << lineno << ": " << matches[0] << '\n';
}
The regex_search(line,matches,pat) searches the line for anything that matches the regular expression
stored in pat and if it finds any matches, it stores them in matches
...
The matches variable is of type smatch
...
The first element, here matches[0], is
the complete match
...
5
...
However, C++ is heavily
used for numerical computation and the standard library reflects that
...
6
...
3)
...
4)
...
For
example:
void f()
{
list
...
begin(),lst
...
0); // calculate the sum
cout << s << '\n';
// print 10014
...
6)
...
6
...
6
...
3
...
, the standard
library complex is a template:
template
class complex {
public:
complex(const Scalar& re ={}, const Scalar& im ={});
//
...
For example:
void f(complex<float> fl, complex
{
complex
db += fl∗3;
fl = pow(1/fl,2);
//
...
For more details, see §40
...
5
...
3 Random Numbers
Random numbers are useful in many contexts, such as testing, games, simulation, and security
...
A random number generator consists of two parts:
[1] an engine that produces a sequence of random or pseudo-random values
...
Examples of distributions are uniform_int_distribution (where all integers produced are equally
likely), normal_distribution (‘‘the bell curve’’), and exponential_distribution (exponential growth);
each for some specified range
...
6
// make a generator
int x = die();
// roll the die: x becomes a value in [1:6]
The standard-library function bind() makes a function object that will invoke its first argument
(here, one_to_six) given its second argument (here, re) as its argument (§33
...
1)
...
130
A Tour of C++: Concurrency and Utilities
Chapter 5
Thanks to its uncompromising attention to generality and performance one expert has deemed the
standard-library random number component ‘‘what every random number library wants to be when
it grows up
...
’’ The using statements makes
what is being done a bit more obvious
...
For novices (of any background) the fully general interface to the random number library can be
a serious obstacle
...
For example:
Rand_int rnd {1,10};
int x = rnd();
// make a random number generator for [1:10]
// x is a number in [1:10]
So, how could we get that? We have to get something like die() inside a class Rand_int:
class Rand_int {
public:
Rand_int(int low, int high) :dist{low,high} { }
int operator()() { return dist(re); }
// draw an int
private:
default_random_engine re;
uniform_int_distribution<> dist;
};
That definition is still ‘‘expert level,’’ but the use of Rand_int() is manageable in the first week of a
C++ course for novices
...
size(); ++i) {
// write out a bar graph
cout << i << '\t';
for (int j=0; j!=mn[i]; ++j) cout << '∗';
cout << endl;
}
}
The output is a (reassuringly boring) uniform distribution (with reasonable statistical variation):
0
1
2
3
4
∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗
∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗
∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗
∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗
∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗
Section 5
...
3
Random Numbers
131
There is no standard graphics library for C++, so I use ‘‘ASCII graphics
...
For more information about random numbers, see §40
...
5
...
4 Vector Arithmetic
The vector described in §4
...
1 was designed to be a general mechanism for holding values, to be
flexible, and to fit into the architecture of containers, iterators, and algorithms
...
Adding such operations to vector would be easy, but its
generality and flexibility precludes optimizations that are often considered essential for serious
numerical work
...
};
The usual arithmetic operations and the most common mathematical functions are supported for
valarrays
...
14+a2/a1;
// numeric array operators *, +, /, and =
a2 += a1∗3
...
}
For more details, see §40
...
In particular,
mensional computations
...
6
...
2
...
2
...
4)
...
7 Advice
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]
[11]
[12]
[13]
Use resource handles to manage resources (RAII); §5
...
Use unique_ptr to refer to objects of polymorphic type; §5
...
1
...
2
...
Use type-safe mechanisms for concurrency; §5
...
Minimize the use of shared data; §5
...
4
...
3
...
Think in terms of concurrent tasks, rather than threads; §5
...
5
...
4
...
4
...
You can write code to explicitly depend on properties of types; §5
...
2
...
5
...
6
...
6
...
Part II
Basic Facilities
This part describes C++’s built-in types and the basic facilities for constructing programs out of them
...
It also discusses the basic facilities for
composing a C++ program out of logical and physical parts
...
I have long entertained a suspicion, with regard to the decisions of philosophers
upon all subjects, and found in myself a greater inclination to dispute, than assent to
their conclusions
...
When a philosopher
has once laid hold of a favourite principle, which perhaps accounts for many natural
effects, he extends the same principle over the whole creation, and reduces to it every
phænomenon, though by the most violent and absurd reasoning
...
PART I
...
– C
...
Parkinson
•
•
•
•
•
•
The ISO C++ Standard
Implementations; The Basic Source Character Set
Types
Fundamental Types; Booleans; Character Types; Integer Types; Floating-Point Types; Prefixes and Suffixes; void; Sizes; Alignment
Declarations
The Structure of Declarations; Declaring Multiple Names; Names; Scope; Initialization;
Deducing a Type: auto and decltype()
Objects and Values
Lvalues and Rvalues; Lifetimes of Objects
Type Aliases
Advice
6
...
In
this book, references to the standard are of the form §iso
...
3
...
1
...
But don’t
expect the standard to be a tutorial or to be easily accessible by non-experts
...
The standard doesn’t say whether a piece of code is good or bad; it
simply says what a programmer can and cannot rely on from an implementation
...
They do so to access system interfaces and
136
Types and Declarations
Chapter 6
hardware features that cannot be expressed directly in C++ or require reliance on specific implementation details
...
This means that
each implementation must provide a specific, well-defined behavior for a construct and that behavior must be documented
...
However, the behavior
of the initialization of c2 is implementation-defined because the number of bits in a char is implementation-defined
...
5
...
1)
...
Other behaviors are unspecified; that is, a range of possible behaviors are acceptable, but the
implementer is not obliged to specify which actually occur
...
For example,
the exact value returned by new is unspecified
...
2)
...
Such behavior is the price we pay for the ability to operate effectively on a large range of
systems
...
However, 16-bit and 32-bit character sets are not uncommon, and machines with
16-bit and 64-bit pointers are in wide use
...
A typical
example of this practice is to present all dependencies on hardware sizes in the form of constants
and type definitions in some header file
...
2)
...
4
...
3)
...
A construct is deemed undefined by the standard if no reasonable
behavior is required by an implementation
...
For example:
const int size = 4∗1024;
char page[size];
void f()
{
page[size+size] = 7; // undefined
}
Plausible outcomes of this code fragment include overwriting unrelated data and triggering a hardware error/exception
...
Where powerful optimizers are used, the actual effects of undefined behavior can become quite
unpredictable
...
1
The ISO C++ Standard
137
unspecified or implementation-defined rather than undefined
...
In many cases, tools exist to help do this
...
1
...
17
...
1
...
A hosted implementation includes all the standard-library facilities as described in the standard (§30
...
A freestanding implementation may provide fewer standard-library facilities, as long as the following are provided:
Freestanding Implementation Headers
Types
Implementation properties
Integer types
Start and termination
Dynamic memory management
Type identification
Exception handling
Initializer lists
Other run-time support
Type traits
Atomics
§10
...
1
§40
...
7
§43
...
2
...
5
§30
...
1
...
3
...
2
...
3
...
4
...
3
Freestanding implementations are meant for code running with only the most minimal operating
system support
...
6
...
2 The Basic Source Character Set
The C++ standard and the examples in this book are written using the basic source character set
consisting of the letters, digits, graphical characters, and whitespace characters from the U
...
variant of the international 7-bit character set ISO 646-1983 called ASCII (ANSI3
...
This can
cause problems for people who use C++ in an environment with a different character set:
• ASCII contains punctuation characters and operator symbols (such as ], {, and !) that are not
available in some character sets
...
• ASCII doesn’t contain characters (such as ñ, Þ, and Æ) that are used for writing languages
other than English
...
2
...
2)
...
2 Types
Consider:
x = y+f(2);
For this to make sense in a C++ program, the names x, y, and f must be suitably declared
...
Every name (identifier) in a C++ program has a type associated with it
...
For example:
float x;
int y = 7;
float f(int);
// x is a floating-point variable
// y is an integer variable with the initial value 7
// f is a function taking an argument of type int and returning a floating-point number
These declarations would make the example meaningful
...
On the other hand, f is declared to be a function that
takes an int as its argument, so it can be called given the interger 2
...
2
...
3)
...
More extensive and
realistic examples are saved for later chapters
...
You must know these elements, plus the terminology and simple syntax that go with them, in order to complete a real project in C++ and especially to read code written by others
...
Consequently, you
may prefer to skim through this chapter, observing the major concepts, and return later as the need
for understanding more details arises
...
2
...
2
...
2
...
2
...
2
...
2
...
2 Pointer types (such as int∗)
§7
...
7 Reference types (such as double& and vector
In addition, a user can define additional types:
§8
...
4 Enumeration types for representing specific sets of values (enum and enum class)
Section 6
...
1
Fundamental Types
139
The Boolean, character, and integer types are collectively called integral types
...
Enumerations and classes (Chapter 16)
are called user-defined types because they must be defined by users rather than being available for
use without previous declaration, the way fundamental types are
...
The standard library provides
many user-defined types (Chapter 4, Chapter 5)
...
2
...
The assumption is that a computer provides bytes for holding characters, words for holding and computing integer values, some entity most suitable for floating-point computation, and
addresses for referring to those entities
...
For most applications, we could use bool for logical values, char for characters, int for integer
values, and double for floating-point values
...
6
...
2 Booleans
A Boolean, bool, can have one of the two values
results of logical operations
...
A Boolean is used to express the
void f(int a, int b)
{
bool b1 {a==b};
//
...
A common use of bool is as the type of the result of a function that tests some condition (a predicate)
...
Conversely, integers can be implicitly converted to bool values: nonzero integers convert to true and 0
converts to false
...
2
...
5)
int i1 = true;
int i2 {true};
// i1 becomes 1
// i2 becomes 1
If you prefer to use the {}-initializer syntax to prevent narrowing, yet still want to convert an int to a
bool, you can be explicit:
140
Types and Declarations
Chapter 6
void f(int i)
{
bool b {i!=0};
//
...
If the result needs to be converted back to bool,
a 0 is converted to false and a nonzero value is converted to true
...
5
...
5)
...
For example:
void g(int∗ p)
{
bool b = p;
bool b2 {p!=nullptr};
true;
// narrows to true or false
// explicit test against nullptr
if (p) {
// equivalent to p!=nullptr
//
...
The shorter form leaves fewer opportunities for mistakes
...
2
...
C++ provides a variety of character types that reflect that – often bewildering – variety:
• char: The default character type, used for program text
...
• signed char: Like char, but guaranteed to be signed, that is, capable of holding both positive
and negative values
...
• wchar_t: Provided to hold characters of a larger character set such as Unicode (see §7
...
2
...
The size of wchar_t is implementation-defined and large enough to hold the largest character
set supported by the implementation’s locale (Chapter 39)
...
• char32_t: A type for holding 32-bit character sets, such as UTF-32
...
5)
...
2
...
A char variable can hold a character of the implementation’s character set
...
Typically, the
character set is a variant of ISO-646, for example ASCII, thus providing the characters appearing
on your keyboard
...
Serious variations occur between character sets supporting different natural languages and
between character sets supporting the same natural language in different ways
...
The larger and more interesting issue of
how to program in a multilingual, multi-character-set environment is beyond the scope of this book,
although it is alluded to in several places (§6
...
3, §36
...
1, Chapter 39)
...
It is not safe to
assume that:
• There are no more than 127 characters in an 8-bit character set (e
...
, some sets provide 255
characters)
...
g
...
• The alphabetic characters are contiguous (EBCDIC leaves a gap between 'i' and 'j')
...
g
...
• A char fits in 1 byte
...
Also, one could reasonably use a 16-bit Unicode encoding for the
basic chars
...
This
general rule applies even to characters
...
For example, the value of 'b' is 98 in the ASCII character set
...
The possibility of converting a char to an integer raises the question: is a char signed or unsigned?
The 256 values represented by an 8-bit byte can be interpreted as the values 0 to 255 or as the values −127 to 127
...
Unfortunately, the choice of signed or unsigned for a plain char is implementationdefined
...
142
Types and Declarations
Chapter 6
Fortunately, the difference matters only for values outside the 0 to 127 range, and the most common
characters are within that range
...
See
§6
...
3
...
Note that the character types are integral types (§6
...
1) so that arithmetic and bitwise logical
operations (§10
...
For example:
void digits()
{
for (int i=0; i!=10; ++i)
cout << static_cast
}
This is a way of writing the ten digits to cout
...
The resulting int is then converted to a char and written to cout
...
6
...
3
...
This opens the
possibility for some nasty surprises and implementation dependencies
...
On an implementation with
8-bit bytes, the answer depends on the meaning of the ‘‘all ones’’ char bit pattern when extended
into an int
...
On a machine where a char
is signed, the answer is −1
...
However, C++ does not offer a general mechanism for detecting this kind
of problem
...
Unfortunately,
some standard-library functions, such as strcmp(), take plain chars only (§43
...
A char must behave identically to either a signed char or an unsigned char
...
For example:
void f(char c, signed char sc, unsigned char uc)
{
char∗ pc = &uc;
// error : no pointer conversion
signed char∗ psc = pc;
// error : no pointer conversion
unsigned char∗ puc = pc;
// error : no pointer conversion
psc = puc;
// error : no pointer conversion
}
Variables of the three char types can be freely assigned to each other
...
5
...
1) is still undefined
...
2
...
1
c = sc;
c = uc;
sc = uc;
uc = sc;
sc = c;
uc = c;
Signed and Unsigned Characters
143
// OK
// implementation-defined if plain chars are signed and if uc’s value is too large
// implementation defined if uc’s value is too large
// OK: conversion to unsigned
// implementation-defined if plain chars are unsigned and if c’s value is too large
// OK: conversion to unsigned
}
To be concrete, assume that a char is 8 bits:
signed char sc = −160;
unsigned char uc = sc;
cout << uc;
// uc == 116 (because 256-160==116)
// print 't'
char count[256];
++count[sc];
++count[uc];
// assume 8-bit chars
// likely disaster: out-of-range access
// OK
None of these potential problems and confusions occur if you use plain
negative character values
...
2
...
2 Character Literals
A character literal is a single character enclosed in single quotes, for example, 'a' and '0'
...
A character literal can be implicitly converted to its integer value in
the character set of the machine on which the C++ program is to run
...
The use of character literals
rather than decimal notation makes programs more portable
...
Despite their appearance, these are single characters
...
There is no limit to the number of hexadecimal digits in the sequence
...
For example:
Octal
Hexadecimal
Decimal
ASCII
'\6'
'\60'
'\137'
'\x6'
'\x30'
'\x05f'
6
48
95
ACK
'0'
'_'
This makes it possible to represent every character in the machine’s character set and, in particular,
to embed such characters in character strings (see §7
...
2)
...
It is possible to enclose more than one character in a character literal, for example, 'ab'
...
The type of such a multicharacter
literal is int
...
The notation is hard enough to read without having to worry about
whether or not the character after a constant is a digit
...
Consider these examples:
char v1[] = "a\xah\129";
char v2[] = "a\xah\127";
char v3[] = "a\xad\127";
char v4[] = "a\xad\0127";
// 6 chars: 'a' '\xa' 'h' '\12' '9' '\0'
// 5 chars: 'a' '\xa' 'h' '\127' '\0'
// 4 chars: 'a' '\xad' '\127' '\0'
// 5 chars: 'a' '\xad' '\012' '7' '\0'
Wide character literals are of the form L'ab' and are of type wchar_t
...
A C++ program can manipulate character sets that are much richer than the 127-character
ASCII set, such as Unicode
...
For example:
U'\UFADEBEEF'
u'\uDEAD'
u'\xDEAD'
The shorter notation u'\uXXXX' is equivalent to U'\U0000XXXX' for any hexadecimal digit X
...
The meaning of the hexadecimal number is defined by the ISO/IEC 10646 standard and such values are called universal
character names
...
2
...
2
...
2
...
3, §iso
...
14
...
E
...
2
...
In addition, integers come in four sizes: short int, ‘‘plain’’ int, long int, and long long int
...
Similarly, short is
a synonym for short int, unsigned for unsigned int, and signed for signed int
...
Section 6
...
4
Integer Types
145
The unsigned integer types are ideal for uses that treat storage as a bit array
...
Attempts to ensure that some values are positive by declaring variables unsigned will typically be
defeated by the implicit conversion rules (§10
...
1, §10
...
2
...
Unlike plain chars, plain ints are always signed
...
If you need more detailed control over integer sizes, you can use aliases from
...
The plain integer types have well-defined minimal sizes (§6
...
8), so the
...
These types must behave like integers and are considered integer types
when considering conversions and integer literal values, but they usually have greater range
(occupy more space)
...
2
...
1 Integer Literals
Integer literals come in three guises: decimal, octal, and hexadecimal
...
3
...
A literal starting with zero followed by x or X (0x or 0X) is a hexadecimal (base 16) number
...
For example:
Decimal
Octal
2
63
83
0
02
077
0123
Hexadecimal
0x0
0x2
0x3f
0x63
The letters a, b, c, d, e, and f, or their uppercase equivalents, are used to represent 10, 11, 12, 13, 14,
and 15, respectively
...
Using these notations to express genuine numbers can lead to surprises
...
Had more bits been used to represent an integer, it would have been the positive decimal number 65535
...
Similarly, the suffix L can be used
to write explicitly long literals
...
Combinations of suffixes are allowed
...
2
...
2)
...
5),
constexpr (§10
...
4) initializers
...
2
...
2 Types of Integer Literals
In general, the type of an integer literal depends on its form, value, and suffix:
• If it is decimal and has no suffix, it has the first of these types in which its value can be represented: int, long int, long long int
...
• If it is suffixed by u or U, its type is the first of these types in which its value can be represented: unsigned int, unsigned long int, unsigned long long int
...
• If it is octal or hexadecimal and suffixed by l or L, its type is the first of these types in which
its value can be represented: long int, unsigned long int, long long int, unsigned long long int
...
• If it is decimal and is suffixed by ll or LL, its type is long long int
...
• If it is suffixed by llu, llU, ull, Ull, LLu, LLU, uLL, or ULL, its type is unsigned long long int
...
Similarly, 0XA000 is of type int on a machine with 32-bit ints but
of type unsigned int on a machine with 16-bit ints
...
6
...
5 Floating-Point Types
The floating-point types represent floating-point numbers
...
There are three floating-point
types: float (single-precision), double (double-precision), and long double (extended-precision)
...
Choosing the right precision for a problem where the choice matters requires significant understanding of floating-point computation
...
6
...
5
...
Again, a compiler ought to warn about floating-point literals that are too large to be represented
...
2
...
1
1
...
23
Floating-Point Literals
0
...
1
...
2e10
1
...
For example, 65
...
43
e
−
147
e−21
is
21
If you want a floating-point literal of type float, you can define one using the suffix f or F:
3
...
0f
2
...
9e−3f
If you want a floating-point literal of type long double, you can define one using the suffix l or L:
3
...
0L
2
...
9e−3L
6
...
6 Prefixes and Suffixes
There is a minor zoo of suffixes indicating types of literals and also a few prefixes:
Arithmetic Literal Prefixes and Suffixes
∗fix
Meaning
Example
Reference
Notation
0
0x
u
l
ll
0X
U
L
LL
f
e
...
2
...
2
§iso
...
14
...
2
...
2
§iso
...
14
...
2
...
2
§iso
...
14
...
2
...
4
§iso
...
14
...
2
...
3
§iso
...
14
...
2
...
3
§iso
...
14
...
2
...
5
§iso
...
14
...
2
...
5
§iso
...
14
...
2
...
5
§iso
...
14
...
3
char
char16_t
char32_t
wchar_t
'c'
u'c'
U'c'
L'c'
string
raw string
UTF-8 string
UTF-16 string
UTF-32 string
wchar_t string
"mess"
R"(\b)"
u8"foo"
u"foo"
U"foo"
L"foo"
§6
...
4
...
2
...
1
§6
...
4
...
2
...
1
§6
...
4
...
2
...
1
§6
...
5
...
2
...
1
§6
...
3
...
2
...
2
§6
...
3
...
2
...
2
§7
...
2
§7
...
2
...
3
...
2
§7
...
2
...
3
...
2
§7
...
2
...
3
...
’’
Obviously, we could also consider
...
However, I consider the nomenclature less important than giving an overview of the
bewildering variety of literals
...
For example:
148
Types and Declarations
1LU
2UL
3ULL
4LLU
5LUL
Chapter 6
// unsigned long
// unsigned long
// unsigned long long
// unsigned long long
// error
The suffixes l and L can be used for floating-point literals to express long double
...
0L
// long int
// long double
Combinations of R, L, and u prefixes are allowed, for example, uR"∗∗(foo\(bar))∗∗"
...
3
...
2)
...
For example, by defining a
user-defined literal operator (§19
...
6), we can get
"foo bar"s
123_km
// a literal of type std::string
// a literal of type Distance
Suffixes not starting with _ are reserved for the standard library
...
2
...
It can, however, be used only as part of a more
complicated type; there are no objects of type void
...
For example:
void x;
void& r;
void f();
void∗ pv;
// error: there are no void objects
// error: there are no references to void
// function f does not return a value (§12
...
4)
// pointer to object of unknown type (§7
...
1)
When declaring a function, you must specify the type of the value returned
...
However, that would make a mess of the grammar (§iso
...
Consequently, void is used as a ‘‘pseudo
return type’’ to indicate that a function doesn’t return a value
...
2
...
1)
...
Why should you bother? People who program on a variety of systems or
use a variety of compilers care a lot because if they don’t, they are forced to waste time finding and
fixing obscure bugs
...
’’ This is a narrow and shortsighted view
...
In addition, programs often need to be compiled with other compilers for the same system,
and even a future release of your favorite compiler may do some things differently from the current
Section 6
...
8
Sizes
149
one
...
It is relatively easy to limit the impact of implementation-dependent language features
...
Using standard-library facilities
wherever feasible is one approach
...
On many machines, there are significant differences in memory requirements, memory access
times, and computation speed among the different varieties of fundamental types
...
Writing truly portable low-level code is harder
...
3
...
2 inch to a byte), a megabyte of memory would stretch about 3 miles (5 km) to
the right
...
The size of an object or type can be obtained using the sizeof operator
(§10
...
This is what is guaranteed about sizes of fundamental types:
•
•
•
•
•
1 ≡ sizeof(char) ≤ sizeof(short) ≤ sizeof(int) ≤ sizeof(long)
1 ≤ sizeof(bool) ≤ sizeof(long)
sizeof(char) ≤ sizeof(wchar_t) ≤ sizeof(long)
sizeof(float) ≤ sizeof(double) ≤ sizeof(long double)
sizeof(N) ≡ sizeof(signed N) ≡ sizeof(unsigned N)
≤ sizeof(long long)
150
Types and Declarations
Chapter 6
In that last line, N can be char, short, int, long, or long long
...
A char can hold a character of
the machine’s character set
...
Similarly, the int type is supposed to be chosen to be the most suitable for holding
and manipulating integers on a given computer; it is typically a 4-byte (32-bit) word
...
For example, there are machines with 32-bit chars
...
Note that it is not guaranteed that
sizeof(long)
For example:
#include
// §40
...
2) are constexpr (§10
...
The fundamental types can be mixed freely in assignments and expressions
...
5)
...
Conversions that are not value-preserving are best avoided (§2
...
2, §10
...
2
...
If you need a specific size of integer, say, a 16-bit integer, you can #include the standard header
...
For example:
int16_t x {0xaabb};
int64_t xxxx {0xaaaabbbbccccdddd};
int_least16_t y;
int_least32_t yy
int_fast32_t z;
// 2 bytes
// 8 bytes
// at least 2 bytes (just like int)
// at least 4 bytes (just like long)
// the fastest int type with at least 4 bytes
The standard header
size in bytes of every object
...
For
example:
void∗ allocate(size_t n); // get n bytes
Similarly,
pointers to get a number of elements
...
2
...
2
...
In addition, on some
machine architectures, the bytes used to hold it must have proper alignment for the hardware to
access it efficiently (or in extreme cases to access it at all)
...
Of course, this is all very implementation specific, and for most programmers
completely implicit
...
Where alignment most often becomes visible is in object layouts: sometimes
structs contain ‘‘holes’’ to improve alignment (§8
...
1)
...
For example:
auto ac = alignof('c');
auto ai = alignof(1);
auto ad = alignof(2
...
Instead, we can use the type specifier alignas: alignas(T) means ‘‘align just like a T
...
size(),bufmax/sizeof(X));
uninitialized_copy(vx
...
begin()+max,buffer);
//
...
3 Declarations
Before a name (identifier) can be used in a C++ program, it must be declared
...
For example:
char ch;
string s;
auto count = 1;
const double pi {3
...
2
...
4
...
5)
// type name
As can be seen from these examples, a declaration can do more than simply associate a type with a
name
...
A definition is a declaration that supplies all
that is needed in a program for the use of an entity
...
A different terminology deems declarations
parts of an interface and definitions parts of an implementation
...
2
...
Assuming that these declarations are in the global scope (§6
...
4), we have:
char ch;
auto count = 1;
const char∗ name = "Njal";
// set aside memory for a char and initialize it to 0
// set aside memory for an int initialized to 1
// set aside memory for a pointer to char
// set aside memory for a string literal "Njal"
// initialize the pointer with the address of that string literal
struct Date { int d, m, y; };
int day(Date∗ p) { return p−>d; }
// Date is a struct with three members
// day is a function that executes the specified code
using Point = std::complex
Of the declarations above, only three are not also definitions:
double sqrt(double);
extern int error_number;
struct User;
// function declaration
// variable declaration
// type name declaration
That is, if used, the entity they refer to must be defined elsewhere
...
*/ }
int error_number = 1;
struct User { /*
...
2
...
However, there can be many declarations
...
So, this fragment has two errors:
int count;
int count;
// error : redefinition
Section 6
...
2):
extern int error_number;
extern int error_number; // OK: redeclaration
Some definitions explicitly specify a ‘‘value’’ for the entities they define
...
1415926535897};
// Point is a name for std::complex
For types, aliases, templates, functions, and constants, the ‘‘value’’ is permanent
...
For example:
void f()
{
int count {1};
// initialize count to 1
const char∗ name {"Bjarne"}; // name is a variable that points to a constant (§7
...
3
...
3
...
Any declaration that specifies a value is a definition
...
3
...
A)
...
However, without too
many radical simplifications, we can consider a declaration as having five parts (in order):
• Optional prefix specifiers (e
...
, static or virtual)
• A base type (e
...
, vector
• A declarator optionally including a name (e
...
, p[7], n, or ∗(∗)[])
• Optional suffix function specifiers (e
...
, const or noexcept)
• An optional initializer or function body (e
...
, ={7,5,3} or {return x;})
Except for function and namespace definitions, a declaration is terminated by a semicolon
...
A specifier is an initial keyword, such as virtual (§3
...
3, §20
...
2), extern (§15
...
2
...
154
Types and Declarations
Chapter 6
A declarator is composed of a name and optionally some declarator operators
...
7
...
7
...
However, ∗, [], and () were
designed to mirror their use in expressions (§10
...
Thus, ∗ is prefix and [] and () are postfix
...
Consequently, char∗kings[] is an array
of pointers to char, whereas char(∗kings)[] is a pointer to an array of char
...
2
...
For example:
const c = 7;
// error : no type
gt(int a, int b) // error : no return type
{
return (a>b) ? a : b;
}
unsigned ui;
long li;
// OK: ‘‘unsigned’’means ‘‘unsigned int’’
// OK: ‘‘long’’ means ‘‘long int’’
In this, standard C++ differs from early versions of C and C++ that allowed the first two examples
by considering int to be the type when none was specified (§44
...
This ‘‘implicit int’’ rule was a
source of subtle errors and much confusion
...
Some type names don’t even look much like names, such as decltype(f(x)) (the return type of a call
f(x); §6
...
6
...
The volatile specifier is described in §41
...
The alignas() specifier is described in §6
...
9
...
3
...
The declaration simply contains a list
of comma-separated declarators
...
3
...
For example:
int∗ p, y;
// int* p; int y; NOT int* y;
int x, ∗q;
// int x; int* q;
int v[10], ∗pv; // int v[10]; int* pv;
Such declarations with multiple names and nontrivial declarators make a program harder to read
and should be avoided
...
3
...
The first character must be a letter
...
C++ imposes no limit on the number of characters in a name
...
Some
run-time environments also make it necessary to extend or restrict the set of characters accepted in
an identifier
...
g
...
A
C++ keyword (§6
...
3
...
Examples of names are:
hello
DEFINED
var0
this_is_a_most_unusually_long_identifier_that_is_better_avoided
foO
bAr
u_name
HorseSense
var1
CLASS
_class
___
Examples of character sequences that cannot be used as identifiers are:
012
pay
...
name
class
if
3var
Nonlocal names starting with an underscore are reserved for special facilities in the implementation
and the run-time environment, so such names should not be used in application programs
...
g
...
17
...
4
...
When reading a program, the compiler always looks for the longest string of characters that
could make up a name
...
Also, elseif is a single name, not the keyword else followed by the keyword if
...
In general, it is best to avoid
names that differ only in subtle ways
...
Consequently, l0, lO, l1, ll, and I1l are poor choices for identifier names
...
Names from a large scope ought to have relatively long and reasonably obvious names, such as
vector, Window_with_border, and Department_number
...
Functions (Chapter 12), classes
(Chapter 16), and namespaces (§14
...
1) can be used to keep scopes small
...
156
Types and Declarations
Chapter 6
Choose names to reflect the meaning of an entity rather than its implementation
...
4)
...
g
...
• The compiler is better at keeping track of types than you are
...
g
...
• Any system of type abbreviations you can come up with will become overelaborate and
cryptic as the variety of types you use increases
...
Try to maintain a consistent naming style
...
Also, use all capitals for macros (if you must use macros (§12
...
Use underscores to separate words in an identifier;
number_of_elements is more readable than numberOfElements
...
Be consistent in your use of abbreviations and acronyms
...
phone_book
6
...
3
...
and_eq
break
class
decltype
else
for
long
not_eq
protected
signed
switch
try
using
xor
asm
case
compl
default
enum
friend
mutable
nullptr
public
sizeof
template
typedef
virtual
xor_eq
auto
catch
const
delete
explicit
goto
namespace
operator
register
static
this
typeid
void
Section 6
...
4
Scope
157
6
...
4 Scope
A declaration introduces a name into a scope; that is, a name can be used only in a specific part of
the program text
...
4) is called a local
name
...
A block is a section of code delimited by a {} pair
...
• Class scope: A name is called a member name (or a class member name) if it is defined in a
class outside any function, class (Chapter 16), enum class (§8
...
1), or other namespace
...
• Namespace scope: A name is called a namespace member name if it is defined in a namespace (§14
...
1) outside any function, lambda (§11
...
4
...
Its scope extends from the point of declaration to the end of
its namespace
...
2)
...
4
...
3
...
The scope of a global name
extends from the point of declaration to the end of the file in which its declaration occurs
...
2)
...
• Statement scope: A name is in a statement scope if it is defined within the () part of a for-,
while-, if-, or switch-statement
...
All names in statement scope are local names
...
6) is in scope from its point of declaration until the end of the
function
...
That is, a name can be redefined to refer to a different entity within a block
...
For example:
int x;
void f()
{
int x;
x = 1;
{
int x;
x = 2;
}
x = 3;
}
int∗ p = &x;
// global x
// local x hides global x
// assign to local x
// hides first local x
// assign to second local x
// assign to first local x
// take address of global x
158
Types and Declarations
Chapter 6
Hiding names is unavoidable when writing large programs
...
Because such errors are relatively rare, they can be very difficult to find
...
Using names such as i and x for global variables or for local variables in a large function is asking
for trouble
...
For example:
int x;
void f2()
{
int x = 1; // hide global x
::x = 2;
// assign to global x
x = 2;
// assign to local x
//
...
The scope of a name that is not a class member starts at its point of declaration, that is, after the
complete declarator and before the initializer
...
For example:
int x = 97;
void f3()
{
int x = x;
}
// perverse: initialize x with its own (uninitialized) value
A good compiler warns if a variable is used before it has been initialized
...
For example:
int x = 11;
void f4()
{
int y = x;
int x = 22;
y = x;
}
// perverse: use of two different objects both called x in a single scope
// use global x: y = 11
// use local x: y = 22
Again, such subtleties are best avoided
...
For example:
void f5(int x)
{
int x;
}
// error
Section 6
...
4
Scope
159
This is an error because x is defined twice in the same scope
...
This
allows us to use conventional names for loop variables repeatedly in a function
...
size(), ++i) cout << v[i] << '\n';
for (auto i : {1, 2, 3, 4, 5, 6, 7}) cout << i << '\n';
}
This contains no name clashes
...
4
...
6
...
5 Initialization
If an initializer is specified for an object, that initializer determines the initial value of an object
...
It is clearer
and less error-prone than the alternatives
...
The two forms using = are what you use in
C
...
For example:
int x1 = 0;
char c1 = 'z';
However, anything much more complicated than that is better done using {}
...
8
...
4)
...
For example,
char to int is allowed, but not int to char
...
For example, float to double is allowed, but not double to float
...
• An integer value cannot be converted to a floating-point type
...
9, x2 becomes 7
char c2 = val2;
// if val2==1025, c2 becomes 1
160
Types and Declarations
Chapter 6
int x3 {val};
char c3 {val2};
// error : possible truncation
// error : possible narrowing
char c4 {24};
char c5 {264};
// OK: 24 can be represented exactly as a char
// error (assuming 8-bit chars): 264 cannot be represented as a char
int x4 {2
...
}
See §10
...
There is no advantage to using {} initialization, and one trap, when using auto to get the type
determined by the initializer
...
3
...
2)
...
It is possible to define a class so that an object can be initialized by a list of values and alternatively be constructed given a couple of arguments that are not simply values to be stored
...
Most types do not
offer such confusing alternatives – even most vectors do not; for example:
vector
vector
// v1 is a vector of 1 element with the value "hello!"
// error : no vector constructor takes a string literal
So, prefer {} initialization over alternatives unless you have a strong reason not to
...
For example:
int x4 {};
double d4 {};
char∗ p {};
vector
string s4 {};
// x4 becomes 0
// d4 becomes 0
...
For integral types, the default value is a suitable representation of
zero
...
2
...
For user-defined types, the default value (if
any) is determined by the type’s constructors (§17
...
3)
...
2
...
Initialization of particular kinds of objects is discussed where appropriate:
• Pointers: §7
...
2, §7
...
2, §7
...
7
...
7
...
3
...
3
...
3
...
4
Classes: §17
...
1 (not using constructors), §17
...
2 (using constructors), §17
...
3 (default),
§17
...
5 (copy and move)
User-defined containers: §17
...
4
6
...
5
...
If you do that
– and that has unfortunately been common – the situation is more complicated
...
The only really good case for an uninitialized variable is a large input buffer
...
get(buf,max); // read at most max characters into buf
We could easily have initialized buf:
char buf[max] {};
// initialize every char to 0
By redundantly initializing, we would have suffered a performance hit which just might have been
significant
...
g
...
If no initializer is specified, a global (§6
...
4), namespace (§14
...
1), local static (§12
...
8), or
static member (§16
...
12) (collectively called static objects) is initialized to {} of the appropriate
type
...
0
Local variables and objects created on the free store (sometimes called dynamic objects or heap
objects; §11
...
3
...
For example:
void f()
{
int x;
char buf[1024];
// x does not have a well-defined value
// buf[i] does not have a well-defined value
int∗ p {new int};
char∗ q {new char[1024]};
string s;
vector
// s=="" because of string’s default constructor
// v=={} because of vector’s default constructor
string∗ ps {new string};
//
...
For example:
void ff()
{
int x {};
char buf[1024]{};
// x becomes 0
// buf[i] becomes 0 for all i
int∗ p {new int{10}};
char∗ q {new char[1024]{}};
// *p becomes 10
// q[i] becomes 0 for all i
//
...
6
...
5
...
More complicated
objects can require more than one value as an initializer
...
For example:
int a[] = { 1, 2 };
struct S { int x, string s };
S s = { 1, "Helios" };
complex
vector
...
1, 2
...
3 };
// array initializer
// struct initializer
// use constructor
// use list constructor
For C-style initialization of arrays, see §7
...
1
...
2
...
3
...
2
...
For initializer-list constructors, see §17
...
4
...
However, some prefer to add it to emphasize that a set of
values are used to initialize a set of member variables
...
3, §16
...
5)
...
3);
// use constructor
// use constructor : v gets 10 elements initialized to 3
...
1)
...
For example:
complex
complex
// function-style initializer (initialization by constructor)
// function declaration
complex
complex
// initialization by constructor to {1,2}
// initialization by constructor to the default value {0,0}
Note that initialization using the {} notation does not narrow (§6
...
5)
...
For example:
auto x1 {1,2,3,4};
// x1 is an initializer_list
auto x2 {1
...
25, 3
...
0,2};
// error: cannot deduce the type of {1
...
3
...
2)
Section 6
...
5
...
3
...
• decltype(expr) for deducing the type of something that is not a simple initializer, such as the
return type for a function or the type of a class member
...
6
...
6
...
Instead, we can let the variable have the type of its initializer
...
That is, auto is a placeholder for the type of
the initializer
...
The
harder the type is to write and the harder the type is to know, the more useful auto becomes
...
begin(); p!=arg
...
begin(); p!=arg
...
Also, it is more resilient
to code changes
...
So, unless there is a good reason not to,
use auto in small scopes
...
That is, compared to
using a specific type, using auto can delay the detection of type errors
...
}
If auto causes surprises, the best cure is typically to make functions smaller, which most often is a
good idea anyway (§12
...
164
Types and Declarations
Chapter 6
We can decorate a deduced type with specifiers and modifiers (§6
...
1), such as const and & (reference; §7
...
For example:
void f(vector
{
for (const auto& x : v) {
//
...
Note that the type of an expression is never a reference because references are implicitly dereferenced in expressions (§7
...
For example:
void g(int& v)
{
auto x = v;
auto& y = v;
}
// x is an int (not an int&)
// y is an int&
6
...
6
...
For example:
char v1 = 12345;
int v2 = 'c';
T v3 = f();
// 12345 is an int
// 'c' is a char
By using the {}-initializer syntax for such definitions, we minimize the chances for unfortunate conversions:
char v1 {12345};
int v2 {'c'};
T v3 {f()};
// error : narrowing
// fine: implicit char->int conversion
// works if and only if the type of f() can be implicitly converted to a T
When we use auto, there is only one type involved, the type of the initializer, and we can safely use
the = syntax:
auto v1 = 12345;
auto v2 = 'c';
auto v3 = f();
// v1 is an int
// v2 is a char
// v3 is of some appropriate type
In fact, it can be an advantage to use the
prise someone:
auto v1 {12345};
auto v2 {'c'};
auto v3 {f()};
=
syntax with
// v1 is a list of int
// v2 is a list of char
// v3 is a list of some appropriate type
This is logical
...
3
...
2
auto x0 {};
auto x1 {1};
auto x2 {1,2};
auto x3 {1,2,3};
auto
and {}-lists
165
// error: cannot deduce a type
// list of int with one element
// list of int with two elements
// list of int with three elements
The type of a homogeneous list of elements of type T is taken to be of type initializer_list
(§3
...
1
...
3
...
In particular, the type of x1 is not deduced to be int
...
’’
6
...
6
...
But sometimes, we want to have a type
deduced without defining an initialized variable
...
This is mostly useful in generic programming
...
What should be
the type of the result of the addition? A matrix, of course, but what might its element type be? The
obvious answer is that the element type of the sum is the type of the sum of the elements
...
1) to be able to express the return type in terms of the arguments: Matrix
...
In the definition, I again need decltype() to express Matrix’s element type:
template
auto operator+(const Matrix
{
Matrix
for (int i=0; i!=a
...
cols(); ++j)
res(i,j) += a(i,j) + b(i,j);
return res;
}
6
...
g
...
g
...
Consequently, we need a name for
‘‘something in memory
...
That is,
an object is a contiguous region of storage; an lvalue is an expression that refers to an object
...
’’ However, not every lvalue may be used on the left-hand side of an assignment; an
166
Types and Declarations
Chapter 6
lvalue can refer to a constant (§7
...
An lvalue that has not been declared const is often called a
modifiable lvalue
...
2
...
3
...
6
...
1 Lvalues and Rvalues
To complement the notion of an lvalue, we have the notion of an rvalue
...
g
...
If you need to be more technical (say, because you want to read the ISO C++ standard), you
need a more refined view of lvalue and rvalue
...
• Movable: The object may be moved from (i
...
, we are allowed to move its value to another
location and leave the object in a valid but unspecified state, rather than copying; §17
...
It turns out that three of the four possible combinations of those two properties are needed to precisely describe the C++ language rules (we have no need for objects that do not have identity and
cannot be moved)
...
The other
alternatives are prvalue (‘‘pure rvalue’’), glvalue (‘‘generalized lvalue’’), and xvalue (‘‘x’’ for ‘‘extraordinary’’ or ‘‘expert only’’; the suggestions for the meaning of this ‘‘x’’ have been quite imaginative)
...
}
// move vs to v2
Here, std::move(vs) is an xvalue: it clearly has identity (we can refer to it as vs), but we have explicitly given permission for it to be moved from by calling std::move() (§3
...
2, §35
...
1)
...
Note
that every expression is either an lvalue or an rvalue, but not both
...
4
...
Objects of types without a declared constructor, such as an int, can be considered to
have default constructors and destructors that do nothing
...
4
...
1
...
2
...
Such objects are sometimes called automatic objects
...
• Static: Objects declared in global or namespace scope (§6
...
4) and statics declared in functions (§12
...
8) or classes (§16
...
12) are created and initialized once (only) and ‘‘live’’ until
the program terminates (§15
...
3)
...
A static object has
the same address throughout the life of a program execution
...
3
...
3)
...
2)
...
g
...
If they
are bound to a reference, their lifetime is that of the reference; otherwise, they ‘‘live’’ until
the end of the full expression of which they are part
...
Typically, temporary objects are automatic
...
2
...
Static and automatic are traditionally referred to as storage classes
...
6
...
Possible reasons include:
• The original name is too long, complicated, or ugly (in some programmer’s eyes)
...
• A specific type is mentioned in one place only to simplify maintenance
...
};
// every container has a value_type
168
Types and Declarations
template
class list {
using value_type = T;
//
...
That is, an
alias refers to the type for which it is an alias
...
4) and classes (Chapter 16)
...
For example:
typedef int int32_t;
typedef short int16_t;
typedef void(∗PtoF)(int);
// equivalent to ‘‘using int32_t = int;’’
// equivalent to ‘‘using int16_t = short;’’
// equivalent to ‘‘using PtoF = void(*)(int);’’
Aliases are used when we want to insulate our code from details of the underlying machine
...
Having written our code in
terms of int32_t, rather than ‘‘plain int,’’ we can port our code to a machine with sizeof(int)==2 by
redefining the single occurrence of int32_t in our code to use a longer integer:
using int32_t = long;
The _t suffix is conventional for aliases (‘‘typedefs’’)
...
7)
...
3
...
The using keyword can also be used to introduce a template alias (§23
...
For example:
template
using Vector = std::vector
We cannot apply type specifiers, such as unsigned, to an alias
...
6 Advice
[1]
[2]
[3]
[4]
[5]
For the final word on language definition issues, see the ISO C++ standard; §6
...
Avoid unspecified and undefined behavior; §6
...
Isolate code that must depend on implementation-defined behavior; §6
...
Avoid unnecessary assumptions about the numeric value of characters; §6
...
3
...
5
...
1
...
2
...
1
...
6
[6]
[7]
[8]
[9]
[10]
[11]
[12]
[13]
[14]
[15]
[16]
[17]
[18]
[19]
[20]
[21]
[22]
[23]
Advice
169
Avoid ‘‘magic constants’’; §6
...
4
...
Avoid unnecessary assumptions about the size of integers; §6
...
8
...
2
...
Prefer plain char over signed char and unsigned char; §6
...
3
...
Beware of conversions between signed and unsigned types; §6
...
3
...
Declare one name (only) per declaration; §6
...
2
...
3
...
Avoid similar-looking names; §6
...
3
...
3
...
Maintain a consistent naming style; §6
...
3
...
3
...
Keep scopes small; §6
...
4
...
3
...
Prefer the {}-initializer syntax for declarations with a named type; §6
...
5
...
3
...
Avoid uninitialized variables; §6
...
5
...
Use an alias to define a meaningful name for a built-in type in cases in which the built-in type
used to represent a value might change; §6
...
Use an alias to define synonyms for types; use enumerations and classes to define new types;
§6
...
This page intentionally left blank
7
Pointers, Arrays, and References
The sublime and the ridiculous
are often so nearly related that
it is difficult to class them separately
...
1 Introduction
This chapter deals with the basic language mechanisms for referring to memory
...
’’ That is, they reside at a
specific address in memory, and an object can be accessed if you know its address and its type
...
172
Pointers, Arrays, and References
Chapter 7
7
...
’’ That is, a variable of type T∗ can hold the address of an
object of type T
...
This operation is also called indirection
...
For example:
char c = 'a';
char∗ p = &c; // p holds the address of c; & is the address-of operator
char c2 = ∗p; // c2 == ’a’; * is the dereference operator
The object pointed to by p is c, and the value stored in c is 'a', so the value of ∗p assigned to c2 is 'a'
...
4)
...
Most machines can address a byte
...
On the other hand, few machines can directly address
an individual bit
...
Note that a bool occupies at least as much space as
a char (§6
...
8)
...
1
...
2
...
2
...
The ∗, meaning ‘‘pointer to,’’ is used as a suffix for a type name
...
3
...
A for the complete grammar
...
5
...
6
...
2
...
A void∗ is used for that
...
’’
Section 7
...
1
void∗
173
A pointer to any type of object can be assigned to a variable of type void∗, but a pointer to function (§12
...
6) cannot
...
Other operations would be unsafe because the compiler cannot know what kind of
object is really pointed to
...
To use a
void∗, we must explicitly convert it to a pointer to a specific type
...
5
...
For example, a machine may assume that every double is allocated on an 8-byte boundary
...
This form of explicit type conversion is inherently unsafe and ugly
...
5
...
The primary use for void∗ is for passing pointers to functions that are not allowed to make
assumptions about the type of the object and for returning untyped objects from functions
...
Functions using void∗ pointers typically exist at the very lowest level of the system, where real
hardware resources are manipulated
...
Where used for optimization, void∗ can be hidden behind
a type-safe interface (§27
...
1)
...
5) and pointers to members (§20
...
7
...
2 nullptr
The literal nullptr represents the null pointer, that is, a pointer that does not point to an object
...
174
Pointers, Arrays, and References
Chapter 7
Before nullptr was introduced, zero (0) was used as a notation for the null pointer
...
Zero (0) is an int
...
5
...
3) allow 0 to be
used as a constant of pointer or pointer-to-member type
...
For example:
int∗ p = NULL; // using the macro NULL
However, there are differences in the definition of NULL in different implementations; for example,
NULL might be 0 or 0L
...
2
...
3
...
7
...
’’ The elements are indexed from 0
to size−1
...
a[31]
You can access an array using the subscript operator, [], or through a pointer (using operator
operator []; §7
...
For example:
∗
or
void f()
{
int aa[10];
aa[6] = 9;
// assign to aa’s 7th element
int x = aa[99]; // undefined behavior
}
Access out of the range of an array is undefined and usually disastrous
...
The number of elements of the array, the array bound, must be a constant expression (§10
...
If
you need variable bounds, use a vector (§4
...
1, §31
...
For example:
void f(int n)
{
int v1[n];
vector
}
// error: array size not a constant expression
// OK: vector with n int elements
Multidimensional arrays are represented as arrays of arrays (§7
...
2)
...
If what
you want is a simple fixed-length sequence of objects of a given type in memory, an array is the
ideal solution
...
Section 7
...
4
...
For example:
int a1[10];
// 10 ints in static storage
void f()
{
int a2 [20];
int∗p = new int[40];
//
...
There is no array assignment, and the name of an array implicitly converts to a pointer to
its first element at the slightest provocation (§7
...
In particular, avoid arrays in interfaces (e
...
, as
function arguments; §7
...
3, §12
...
2) because the implicit conversion to pointer is the root cause of
many common errors in C code and C-style C++ code
...
2
...
That’s most easily and
most reliably done by having the lifetime of the free-store array controlled by a resource handle
(e
...
, string (§19
...
3), vector (§13
...
2), or unique_ptr (§34
...
1))
...
Obviously, C programmers cannot follow
these pieces of advice because C lacks the ability to encapsulate arrays, but that doesn’t make the
advice bad in the context of C++
...
That’s the way
C stores strings, so a zero-terminated array of char is often called a C-style string
...
3
...
g
...
4) rely on it
...
7
...
1 Array Initializers
An array can be initialized by a list of values
...
Consequently, v1 and v2 are of type int[4] and
char[4], respectively
...
For example:
char v3[2] = { 'a', 'b', 0 };
char v4[3] = { 'a', 'b', 0 };
// error : too many initializers
// OK
If the initializer supplies too few elements for an array, 0 is used for the rest
...
You cannot initialize one array with another (not
even of exactly the same type), and there is no array assignment:
int v6[8] = v5; // error: can’t copy an array (cannot assign an int* to an array)
v6 = v5;
// error : no array assignment
Similarly, you can’t pass arrays by value
...
4
...
4
...
6, §34
...
2
...
5) instead
...
3
...
7
...
2 String Literals
A string literal is a character sequence enclosed within double quotes:
"this is a string"
A string literal contains one more character than it appears to have; it is terminated by the null character, '\0', with the value 0
...
In C and in older C++ code, you could assign a string literal to a non-const char∗:
void f()
{
char∗ p = "Plato";
p[4] = 'e';
}
// error, but accepted in pre-C++11-standard code
// error: assignment to const
It would obviously be unsafe to accept that assignment
...
Having string literals immutable is not only obvious but also allows implementations to do significant optimizations
in the way string literals are stored and accessed
...
For example:
const char∗ error_message(int i)
{
//
...
3
...
Whether two identical string literals are allocated as one array or as two is implementationdefined (§6
...
For example:
const char∗ p = "Heraclitus";
const char∗ q = "Heraclitus";
void g()
{
if (p == q) cout << "one!\n";
//
...
The empty string is written as a pair of adjacent double quotes, "", and has the type const
char[1]
...
The backslash convention for representing nongraphic characters (§6
...
3
...
This makes it possible to represent the double quote (") and the escape character
backslash (\) within a string
...
For example:
cout<<"beep at end of message\a\n";
The escape character, '\a', is the ASCII character BEL (also known as alert), which causes a sound
to be emitted
...
For example:
char alpha[] = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
The compiler will concatenate adjacent strings, so alpha could equivalently have been initialized by
the single string
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
It is possible to have the null character in a string, but most programs will not suspect that there are
characters after it
...
4
...
3
...
1 Raw Character Strings
To represent a backslash (\) or a double quote (") in a string literal, we have to precede it with a
backslash
...
However, if we need a lot of backslashes
and a lot of quotes in string literals, this simple technique becomes unmanageable
...
1
...
This is a convention shared by many programming languages, so we can’t just change it
...
Consider how to write the pattern representing two words separated by a
backslash (\):
string s = "\\w\\\\w";
// I hope I got that right
To prevent the frustration and errors caused by this clash of conventions, C++ provides raw string
literals
...
The initial R is there
to distinguish raw string literals from ordinary string literals
...
For example:
R"("quoted string")"
// the string is "quoted string"
So, how do we get the character sequence )" into a raw string literal? Fortunately, that’s a rare
problem, but "( and )" is only the default delimiter pair
...
For example:
R"∗∗∗("quoted string containing the usual terminator ("))")∗∗∗"
// "quoted string containing the usual terminator ("))"
The character sequence after the ) must be identical to the sequence before the (
...
Unless you work with regular expressions, raw string literals are probably just a curiosity (and
one more thing to learn), but regular expressions are useful and widely used
...
)∗\")|"
// Are the five backslashes correct or not?
With examples like that, even experts easily become confused, and raw string literals provide a significant service
...
For example:
string counts {R"(1
22
333)"};
is equivalent to
string x {"1\n22\n333"};
7
...
2
...
2
...
Its type is const
wchar_t[]
...
3
...
1) of
wide characters of type const wchar_t[]
...
Section 7
...
2
...
This sounds
excessive, but there are three major encodings of Unicode: UTF-8, UTF-16, and UTF-32
...
All three UTF encodings support all Unicode characters, so which you use depends on the system you need to fit into
...
g
...
UTF-8 is a variable-width encoding: common characters fit into 1 byte, less frequently used
characters (by some estimate of use) into 2 bytes, and rarer characters into 3 or 4 bytes
...
The various Latin alphabets, Greek, Cyrillic, Hebrew, Arabic, and more fit into 2 bytes
...
We can represent an ordinary English character string in a variety of ways
...
Obviously, the real purpose of Unicode strings is to be able to put Unicode characters into them
...
"
Printing that string appropriately gives you
The official vowels in Danish are: a, e, i, o, u, æ, ø, å and y
...
2
...
3) [Unicode,1996]
...
For example, u'0430' (Cyrillic lowercase letter ‘‘a’’) is the
2-byte hexadecimal value D0B0 in UTF-8, the 2-byte hexadecimal value 0403 in UTF-16, and the
4-byte hexadecimal value 00000403 in UTF-32
...
The order of the us and Rs and their cases are significant: RU and Ur are not valid string prefixes
...
4 Pointers into Arrays
In C++, pointers and arrays are closely related
...
For example:
180
Pointers, Arrays, and References
Chapter 7
int v[] = { 1, 2, 3, 4 };
int∗ p1 = v;
// pointer to initial element (implicit conversion)
int∗ p2 = &v[0];
// pointer to initial element
int∗ p3 = v+4;
// pointer to one-beyond-last element
or graphically:
p1
v:
p2
1
2
3
p3
4
Taking a pointer to the element one beyond the end of an array is guaranteed to work
...
5, §33
...
However, since such a pointer does not in fact point
to an element of the array, it may not be used for reading or writing
...
For example:
int∗ p4 = v−1; // before the beginning, undefined: don’t do it
int∗ p5 = v+7; // beyond the end, undefined: don’t do it
The implicit conversion of an array name to a pointer to the initial element of the array is extensively used in function calls in C-style code
...
h>
void f()
{
char v[] = "Annemarie";
char∗ p = v;
// implicit conversion of char[] to char*
strlen(p);
strlen(v);
// implicit conversion of char[] to char*
v = p;
// error: cannot assign to array
}
The same value is passed to the standard-library function strlen() in both calls
...
In other words, there is no way of declaring a function
so that the array v is copied when the function is called
...
The implicit conversion of the array argument to a pointer means that the size of the array is lost
to the called function
...
Like other C standard-library functions taking pointers to characters, strlen()
relies on zero to indicate end-of-string; strlen(p) returns the number of characters up to and not
including the terminating 0
...
The standard-library vector (§4
...
1, §13
...
4), array (§8
...
4, §34
...
1), and string (§4
...
These library types
give their number of elements as their size() without having to count elements each time
...
4
...
4
...
5, Chapter 32)
...
For example:
void fi(char v[])
{
for (int i = 0; v[i]!=0; ++i)
use(v[i]);
}
void fp(char v[])
{
for (char∗ p = v; ∗p!=0; ++p)
use(∗p);
}
The prefix ∗ operator dereferences a pointer so that ∗p is the character pointed to by p, and ++ increments the pointer so that it refers to the next element of the array
...
With modern compilers, identical code should be (and usually is) generated for both examples
...
Subscripting a built-in array is defined in terms of the pointer operations + and ∗
...
For example, 3["Texas"]=="Texas"[3]=='a'
...
These equivalences are pretty low-level and do not
hold for standard-library containers, such as array and vector
...
When an arithmetic operator is applied to a pointer p of type T∗, p is assumed
to point to an element of an array of objects of type T; p+1 points to the next element of that array,
and p−1 points to the previous element
...
For example:
template
int byte_diff(T∗ p, T∗ q)
{
return reinterpret_cast
}
void diff_test()
{
int vi[10];
short vs[10];
182
Pointers, Arrays, and References
Chapter 7
cout << vi << ' ' << &vi[1] << ' ' << &vi[1]−&vi[0] << ' ' << byte_diff(&vi[0],&vi[1]) << '\n';
cout << vs << ' ' << &vs[1] << ' ' << &vs[1]−&vs[0] << ' ' << byte_diff(&vs[0],&vs[1]) << '\n';
}
This produced:
0x7fffaef0 0x7fffaef4 1 4
0x7fffaedc 0x7fffaede 1 2
The pointer values were printed using the default hexadecimal notation
...
Subtraction of pointers is defined only when both pointers point to elements of the same array
(although the language has no fast way of ensuring that is the case)
...
One can add an integer to a pointer or subtract an integer from a pointer; in both cases, the
result is a pointer value
...
For example:
void f()
{
int v1[10];
int v2[10];
int i1 = &v1[5]−&v1[3];
int i2 = &v1[5]−&v2[3];
// i1 = 2
// result undefined
int∗ p1 = v2+2;
int∗ p2 = v2−2;
// p1 = &v2[2]
// *p2 undefined
}
Complicated pointer arithmetic is usually unnecessary and best avoided
...
Arrays are not self-describing because the number of elements of an array is not guaranteed to
be stored with the array
...
For example:
void fp(char v[], int size)
{
for (int i=0; i!=size; ++i)
use(v[i]);
for (int x : v)
use(x);
const int N = 7;
char v2[N];
for (int i=0; i!=N; ++i)
use(v2[i]);
for (int x : v2)
use(x);
}
// hope that v has at least size elements
// error : range-for does not work for pointers
// range-for works for arrays of known size
Section 7
...
1
Navigating Arrays
183
This array concept is inherently low-level
...
2
...
2
...
Some C++ implementations offer optional range checking for arrays
...
If you are not using range checking for individual accesses, try to maintain a consistent policy of accessing elements only in well-defined ranges
...
7
...
2 Multidimensional Arrays
Multidimensional arrays are represented as arrays of arrays; a 3-by-5 array is declared like this:
int ma[3][5];
// 3 arrays with 5 ints each
We can initialize ma like this:
void init_ma()
{
for (int i = 0; i!=3; i++)
for (int j = 0; j!=5; j++)
ma[i][j] = 10∗i+j;
}
or graphically:
ma:
00 01 02 03 04 10 11 12 13 14 20 21 22 23 24
The array ma is simply 15 ints that we access as if it were 3 arrays of 5 ints
...
The dimensions 3
and 5 exist in the compiler source only
...
For example, we might print ma like this:
void print_ma()
{
for (int i = 0; i!=3; i++) {
for (int j = 0; j!=5; j++)
cout << ma[i][j] << '\t';
cout << '\n';
}
}
The comma notation used for array bounds in some languages cannot be used in C++ because the
comma (,) is a sequencing operator (§10
...
2)
...
For example:
int bad[3,5];
int good[3][5];
int ouch = good[1,4];
int nice = good[1][4];
// error: comma not allowed in constant expression
// 3 arrays with 5 ints each
// error: int initialized by int* (good[1,4] means good[4], which is an int*)
184
Pointers, Arrays, and References
Chapter 7
7
...
3 Passing Arrays
Arrays cannot directly be passed by value
...
For example:
void comp(double arg[10])
{
for (int i=0; i!=10; ++i)
arg[i]+=99;
}
// arg is a double*
void f()
{
double a1[10];
double a2[5];
double a3[100];
comp(a1);
comp(a2);
comp(a3);
// disaster!
// uses only the first 10 elements
};
This code looks sane, but it is not
...
Also, anyone who guessed that the array was passed by value will be disappointed:
the writes to arg[i] are writes directly to the elements of comp()’s argument, rather than to a copy
...
When used as a function argument, the first dimension of
an array is simply treated as a pointer
...
This implies
that if you want to pass a sequence of elements without losing size information, you should not
pass a built-in array
...
If you insist on using arrays directly, you will have to deal with bugs and confusion without getting noticeable advantages in return
...
If the dimensions are known at compile time, there is no problem:
void print_m35(int m[3][5])
{
for (int i = 0; i!=3; i++) {
for (int j = 0; j!=5; j++)
cout << m[i][j] << '\t';
cout << '\n';
}
}
Section 7
...
3
Passing Arrays
185
A matrix represented as a multidimensional array is passed as a pointer (rather than copied; §7
...
The first dimension of an array is irrelevant to finding the location of an element; it simply states
how many elements (here, 3) of the appropriate type (here, int[5]) are present
...
The first dimension can therefore be passed as an argument:
void print_mi5(int m[][5], int dim1)
{
for (int i = 0; i!=dim1; i++) {
for (int j = 0; j!=5; j++)
cout << m[i][j] << '\t';
cout << '\n';
}
}
When both dimensions need to be passed, the ‘‘obvious solution’’ does not work:
void print_mij(int m[][], int dim1, int dim2)
// doesn’t behave as most people would think
{
for (int i = 0; i!=dim1; i++) {
for (int j = 0; j!=dim2; j++)
cout << m[i][j] << '\t';
// sur prise!
cout << '\n';
}
}
Fortunately, the argument declaration m[][] is illegal because the second dimension of a multidimensional array must be known in order to find the location of an element
...
A correct solution is:
void print_mij(int∗ m, int dim1, int dim2)
{
for (int i = 0; i!=dim1; i++) {
for (int j = 0; j!=dim2; j++)
cout << m[i∗dim2+j] << '\t'; // obscure
cout << '\n';
}
}
The expression used for accessing the members in print_mij() is equivalent to the one the compiler
generates when it knows the last dimension
...
This kind of subtle and messy code is best hidden
...
In that way, you might ease the
task of the next programmer to touch the code
...
2
...
5
...
The standard vector (§31
...
7
...
2
...
4)
...
2
...
Basically, constexpr’s role is to enable and ensure compile-time evaluation, whereas const’s primary role is to specify immutability in interfaces
...
Many objects don’t have their values changed after initialization:
• Symbolic constants lead to more maintainable code than using literals directly in code
...
• Most function parameters are read but not written to
...
For example:
const int model = 90;
const int v[] = { 1, 2, 3, 4 };
const int x;
// model is a const
// v[i] is a const
// error : no initializer
Because an object declared const cannot be assigned to, it must be initialized
...
For example:
void g(const X∗ p)
{
// can’t modify *p here
}
Section 7
...
}
Pointers and const
187
// val can be modified here
When using a pointer, two objects are involved: the pointer itself and the object pointed to
...
To
declare a pointer itself, rather than the object pointed to, to be a constant, we use the declarator
operator ∗const instead of plain ∗
...
There is no const∗ declarator operator, so a const appearing before the ∗ is taken to be part of the base type
...
’’
An object that is a constant when accessed through one pointer may be variable when accessed
in other ways
...
By declaring a pointer argument
const, the function is prohibited from modifying the object pointed to
...
The second version is used for mutable strings
...
However, the address of a constant cannot be assigned to an unrestricted pointer
because this would allow the object’s value to be changed
...
2
...
5)
...
6 Pointers and Ownership
A resource is something that has to be acquired and later released (§5
...
Memory acquired by new
and released by delete (§11
...
2) are examples of resources where the most direct handle to the resource is a pointer
...
Consider:
void confused(int∗ p)
{
// delete p?
}
int global {7};
void f()
{
X∗ pn = new int{7};
int i {7};
int q = &i;
confused(pn);
confused(q);
confused(&global);
}
If confused() deletes p the program will seriously misbehave for the second two calls because we
may not delete objects not allocated by new (§11
...
If confused() does not delete p the program
leaks (§11
...
1)
...
It is usually a good idea to immediately place a pointer that represents ownership in a resource
handle class, such as vector, string, and unique_ptr
...
Chapter 13 discusses
resource management in greater detail
...
7
References
189
7
...
The type of the pointer determines what can
be done to the data through the pointer
...
m
...
• We must be more careful when using pointers than when using an object directly: a pointer
may be a nullptr or point to an object that wasn’t the one we expected
...
Worse, managing pointer variables with varying values and protecting code against the possibility of nullptr can be a significant burden
...
The language mechanism addressing these problems is
called a reference
...
• A reference always refers to the object to which it was initialized
...
7
...
A reference is an alternative name for an object, an alias
...
For example:
template
class vector {
T∗ elem;
//
...
// pass element to be added by reference
};
void f(const vector
{
double d1 = v[1];
// copy the value of the double referred to by v
...
operator[](2)
v
...
190
Pointers, Arrays, and References
Chapter 7
To reflect the lvalue/rvalue and const/non-const distinctions, there are three kinds of references:
• lvalue references: to refer to objects whose value we want to change
• const references: to refer to objects whose value we do not want to change (e
...
, a constant)
• rvalue references: to refer to objects whose value we do not need to preserve after we have
used it (e
...
, a temporary)
Collectively, they are called references
...
7
...
1 Lvalue References
In a type name, the notation X& means ‘‘reference to X
...
For example:
void f()
{
int var = 1;
int& r {var};
int x = r;
r = 2;
// r and var now refer to the same int
// x becomes 1
// var becomes 2
}
To ensure that a reference is a name for something (that is, that it is bound to an object), we must
initialize the reference
...
Despite appearances, no operator operates on a reference
...
Consequently, the value of a reference cannot be changed after initialization; it always
refers to the object it was initialized to denote
...
Thus, we cannot have a pointer to a reference
...
In that sense, a reference is not an object
...
It doesn’t do much harm to think about references that way, as long as one remembers that a reference isn’t an object that can be manipulated the way a pointer is:
Section 7
...
1
Lvalue References
pp:
191
&ii
rr:
ii:
1
In some cases, the compiler can optimize away a reference so that there is no object representing
that reference at run time
...
4)
...
The initializer for a const T& need not be an lvalue or even of type T
...
5)
...
[3] Finally, this temporary variable is used as the value of the initializer
...
References to variables and references to constants are distinguished because introducing a temporary for a variable would have been highly error-prone; an assignment to the variable would
become an assignment to the – soon-to-disappear – temporary
...
2
...
A reference can be used to specify a function argument so that the function can change the
value of an object passed to it
...
To keep a program readable, it is often best to
avoid functions that modify their arguments
...
Consequently, ‘‘plain’’ reference arguments should be used only where the name of
the function gives a strong hint that the reference argument is modified
...
This is mostly used to define functions that can be
used on both the left-hand and right-hand sides of an assignment
...
For
example:
template
class Map {
// a simple map class
public:
V& operator[](const K& v);
// return the value corresponding to the key v
pair
pair
...
4
...
4
...
first)
return x
...
push_back({k,V{}});
return elem
...
second;
// add pair at end (§4
...
2)
// return the (default) value of the new element
}
I pass the key argument, k, by reference because it might be of a type that is expensive to copy
...
I use a const reference for k because I don’t want to modify it and because I might want to use a literal or a temporary object as an argument
...
For example:
Section 7
...
1
Lvalue References
193
int main() // count the number of occurrences of each word on input
{
Map
for (string s; cin>>s;) ++buf[s];
for (const auto& x : buf)
cout << x
...
second << '\n';
}
Each time around, the input loop reads one word from the standard input stream cin into the string s
(§4
...
2) and then updates the counter associated with it
...
For example, given the input
aa bb bb aa aa bb aa aa
this program will produce
aa: 5
bb: 3
The range- for loop works for this because
standard-library map
...
7
...
• A const lvalue reference refers to a constant, which is immutable from the point of view of
the user of the reference
...
We want to know if a reference refers to a temporary, because if it does, we can sometimes turn an
expensive copy operation into a cheap move operation (§3
...
2, §17
...
5
...
An object (such as
a string or a list) that is represented by a small descriptor pointing to a potentially huge amount of
information can be simply and cheaply moved if we know that the source isn’t going to be used
again
...
3
...
An rvalue reference can bind to an rvalue, but not to an lvalue
...
For example:
string var {"Cambridge"};
string f();
string& r1 {var};
string& r2 {f()};
string& r3 {"Princeton"};
// lvalue reference, bind r1 to var (an lvalue)
// lvalue reference, error : f() is an rvalue
// lvalue reference, error : cannot bind to temporar y
194
Pointers, Arrays, and References
string&& rr1 {f()};
string&& rr2 {var};
string&& rr3 {"Oxford"};
Chapter 7
// rvalue reference, fine: bind rr1 to rvalue (a temporar y)
// rvalue reference, error : var is an lvalue
// rr3 refers to a temporar y holding "Oxford"
const string cr1& {"Harvard"}; // OK: make temporar y and bind to cr1
The && declarator operator means ‘‘rvalue reference
...
Both a
const lvalue reference and an rvalue reference can bind to an rvalue
...
• We use a const lvalue reference to prevent modification of an argument
...
For example:
string f(string&& s)
{
if (s
...
Consider:
template
swap(T& a, T& b)
// "old-style swap"
{
T tmp {a}; // now we have two copies of a
a = b;
// now we have two copies of b
b = tmp; // now we have two copies of tmp (aka a)
}
If T is a type for which it can be expensive to copy elements, such as string and vector, this swap()
becomes an expensive operation
...
We can tell that to the compiler:
template
void swap(T& a, T& b)
// "perfect swap" (almost)
{
T tmp {static_cast
a = static_cast
// the assignment may write to b
b = static_cast
}
The result value of static_cast
...
In particular, if a type T has a move constructor (§3
...
2, §17
...
2) or a move assignment, it will be used
...
7
...
vector(const vector& r); // copy constructor (copy r’s representation)
vector(vector&& r);
// move constructor ("steal" representation from r)
};
vector
vector
vector
// s is an lvalue, so use copy constructor
// s+"tail" is an rvalue so pick move constructor
The use of static_cast in swap() is a bit verbose and slightly prone to mistyping, so the standard
library provides a move() function: move(x) means static_cast
...
Since move(x) does not move x (it simply produces an rvalue reference to x), it would have been
better if move() had been called rval(), but by now move() has been used for years
...
Consider:
void f(vector
{
swap(v,vector
//
...
A solution is to augment it by two overloads:
template
template
Our example will be handled by that last version of swap()
...
(§31
...
3) to handle the most
common cases of rvalue arguments to swap():
void f(string& s, vector
{
s
...
capacity()==s
...
capacity()==s
...
clear();
swap(v
...
5
...
1, §35
...
1)
...
3
...
Also, their operations that insert new elements, such as insert() and push_back(), have versions that
take rvalue references
...
7
...
But what kind of reference? Lvalue reference or rvalue
reference? Consider:
using rr_i = int&&;
using lr_i = int&;
using rr_rr_i = rr_i&&;
using lr_rr_i = rr_i&;
using rr_lr_i = lr_i&&;
using lr_lr_i = lr_i&;
// ‘‘int && &&’’ is an int&&
// ‘‘int && &’’ is an int&
// ‘‘int & &&’’ is an int&
// ‘‘int & &’’ is an int&
In other words, lvalue reference always wins
...
This is sometimes known as reference
collapse
...
4
...
5) or a template type
argument (§23
...
2
...
7
...
4 Pointers and References
Pointers and references are two mechanisms for referring to an object from different places in a
program without copying
...
If you need to change which object to refer to, use a pointer
...
1
...
For example:
Section 7
...
4
Pointers and References
197
void fp(char∗ p)
{
while (∗p)
cout << ++∗p;
}
void fr(char& r)
{
while (r)
cout << ++r;
// oops: increments the char referred to, not the reference
// near-infinite loop!
}
void fr2(char& r)
{
char∗ p = &r;
// get a pointer to the object referred to
while (∗p)
cout << ++∗p;
}
Conversely, if you want to be sure that a name always refers to the same object, use a reference
...
};
// Proxy refers to the object with which it is initialized
template
T∗ m;
public:
Proxy(T∗ mm) :m{mm} {}
void rebind(T∗ mm) { m = mm; }
//
...
1) on something that refers to an
object, use a reference:
Matrix operator+(const Matrix&, const Matrix&);
Matrix operator−(const Matrix∗, const Matrix∗);
// OK
// error : no user-defined type argument
Matrix y, z;
//
...
2
...
198
Pointers, Arrays, and References
Chapter 7
If you want a collection of something that refers to an object, you must use a pointer:
int x, y;
string& a1[] = {x, y};
string∗ a2[] = {&x, &y};
vector
vector
// error : array of references
// OK
// error : vector of references
// OK
Once we leave the cases where C++ leaves no choice for the programmer, we enter the domain of
aesthetics
...
If you need a notion of ‘‘no value,’’ pointers offer nullptr
...
For example:
void fp(X∗ p)
{
if (p == nullptr) {
// no value
}
else {
// use *p
}
}
void fr(X& r) // common style
{
// assume that r is valid and use it
}
If you really want to, you can construct and check for a ‘‘null reference’’ for a particular type:
void fr2(X& r)
{
if (&r == &nullX) {
// no value
}
else {
// use r
}
}
// or maybe r==nullX
Obviously, you need to have suitably defined nullX
...
A programmer is allowed to assume that a reference is valid
...
For example:
char∗ ident(char ∗ p) { return p; }
char& r {∗ident(nullptr)}; // invalid code
This code is not valid C++ code
...
Section 7
...
8 Advice
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]
[11]
[12]
[13]
[14]
Keep use of pointers simple and straightforward; §7
...
1
...
4
...
4
...
Avoid multidimensional arrays; define suitable containers instead; §7
...
2
...
2
...
Use containers (e
...
, vector, array, and valarray) rather than built-in (C-style) arrays; §7
...
1
...
4
...
3
...
1
...
7
...
Use rvalue references (only) for forwarding and move semantics; §7
...
2
...
6
...
2
...
Use const pointers and const references to express immutability in interfaces; §7
...
Prefer references to pointers as arguments, except where ‘‘no object’’ is a reasonable option;
§7
...
4
...
– The people
•
•
•
•
•
Introduction
Structures
struct Layout; struct Names; Structures and Classes; Structures and Arrays; Type Equivalence; Plain Old Data; Fields
Unions
Unions and Classes; Anonymous unions
Enumerations
enum classes; Plain enums; Unnamed enums
Advice
8
...
This chapter introduces the three most primitive variants of the notion of a user-defined type:
• A struct (a structure) is a sequence of elements (called members) of arbitrary types
...
• An enum (an enumeration) is a type with a set of named constants (called enumerators)
...
Variants of these kinds of simple types have existed since the earliest days of C++
...
The notion of a struct as described here is a simple form of a class (§3
...
202
Structures, Unions, and Enumerations
Chapter 8
8
...
In its simplest form, a struct is an aggregate
of elements of arbitrary types
...
Note the terminating semicolon
...
(dot) operator
...
name = "Jim Dandy";
jd
...
3
...
For example:
Address jd = {
"Jim Dandy",
61, "South St",
"New Providence",
{'N','J'}, "07974"
};
Note that jd
...
Strings are terminated by a zero character, '\0', so "NJ" has three characters – one more than will fit into jd
...
I deliberately use rather
low-level types for the members to illustrate how that can be done and what kinds of problems it
can cause
...
For example:
void print_addr(Address∗ p)
{
cout << p−>name << '\n'
<< p−>number << ' ' << p−>street << '\n'
<< p−>town << '\n'
<< p−>state[0] << p−>state[1] << ' ' << p−>zip << '\n';
}
When p is a pointer, p−>m is equivalent to (∗p)
...
Section 8
...
203
(struct member
void print_addr2(const Address& r)
{
cout << r
...
number << ' ' << r
...
town << '\n'
<< r
...
state[1] << ' ' << r
...
2
...
For example:
Address current;
Address set_current(Address next)
{
address prev = current;
current = next;
return prev;
}
Other plausible operations, such as comparison (== and !=), are not available by default
...
2
...
1, Chapter 18)
...
2
...
For example, we might store
primitive equipment readout in a structure like this:
struct Readout {
char hour;
int value;
char seq;
};
// [0:23]
// sequence mark ['a':'z']
You could imagine the members of a Readout object laid out in memory like this:
hour: value:
seq:
Members are allocated in memory in declaration order, so the address of hour must be less than the
address of value
...
2
...
However, the size of an object of a struct is not necessarily the sum of the sizes of its members
...
For example, integers are often allocated on word boundaries
...
2
...
This leads to ‘‘holes’’ in the structures
...
You can minimize wasted space by simply ordering members by size (largest member first)
...
The
reason is that we need to maintain alignment when we put two objects next to each other, say, in an
array of Readouts
...
It is usually best to order members for readability and sort them by size only if there is a
demonstrated need to optimize
...
e
...
5)
...
2
...
For example:
struct Link {
Link∗ previous;
Link∗ successor;
};
However, it is not possible to declare new objects of a struct until its complete declaration has been
seen
...
To allow two (or
Section 8
...
2
struct
Names
205
more) structs to refer to each other, we can declare a name to be the name of a struct
...
The name of a struct can be used before the type is defined as long as that use does not require
the name of a member or the size of the structure to be known
...
For example:
struct S; // ‘‘S’’ is the name of some type
extern S a;
S f();
void g(S);
S∗ h(S∗);
However, many such declarations cannot be used unless the type S is defined:
void k(S∗ p)
{
S a;
// error: S not defined; size needed to allocate
f();
g(a);
p−>m = 7;
// error: S not defined; size needed to return value
// error: S not defined; size needed to pass argument
// error: S not defined; member name not known
S∗ q = h(p);
q−>m = 7;
// ok: pointers can be allocated and passed
// error: S not defined; member name not known
}
For reasons that reach into the prehistory of C, it is possible to declare a struct and a non-struct with
the same name in the same scope
...
*/ };
int stat(char∗ name, struct stat∗ buf);
In that case, the plain name (stat) is the name of the non-struct, and the struct must be referred to
with the prefix struct
...
3), and enum (§8
...
However, it is best not to overload names to make such explicit disambiguation necessary
...
2
...
So, a struct can have member
functions (§2
...
2, Chapter 16)
...
For example:
struct Points {
vector
Points(Point p0) { elem
...
push_back(p0); elem
...
};
Points x0;
Points x1{ {100,200} };
Points x1{ {100,200}, {300,400} };
// error : no default constructor
// one Point
// two Points
You do not need to define a constructor simply to initialize members in order
...
3
...
1)
// default construction: {{},{}}; that is {0
...
4
...
2, §13
...
For example:
struct Address {
string name;
int number;
string street;
string town;
char state[2];
char zip[5];
// "Jim Dandy"
// 61
// "South St"
// "New Providence"
// ’N’ ’J’
// 07974
Address(const string n, int nu, const string& s, const string& t, const string& st, int z);
};
Here, I added a constructor to ensure that every member was initialized and to allow me to use a
string and an int for the postal code, rather than fiddling with individual characters
...
2
...
1)
The Address constructor might be defined like this:
Section 8
...
3
Structures and Classes
207
Address::Address(const string& n, int nu, const string& s, const string& t, const string& st, int z)
// validate postal code
:name{n},
number{nu},
street{s},
town{t}
{
if (st
...
4
...
str()};
switch (zi
...
check that the code makes sense
...
2
...
For example:
struct Point {
int x,y
};
Point points[3] {{1,2},{3,4},{5,6}};
int x2 = points[2]
...
elem[2]
...
For
example:
208
Structures, Unions, and Enumerations
Chapter 8
Array shift(Array a, Point p)
{
for (int i=0; i!=3; ++i) {
a
...
x += p
...
elem[i]
...
y;
}
return a;
}
Array ax = shift(points2,{10,20});
The notation for Array is a bit primitive: Why i!=3? Why keep repeating
...
2
...
2
...
};
This array is a template to allow arbitrary numbers of elements of arbitrary types
...
5
...
1) and const objects (§16
...
9
...
Using array,
we can now write:
struct Point {
int x,y
};
using Array = array
Array points {{1,2},{3,4},{5,6}};
int x2 = points[2]
...
y;
Section 8
...
4
Structures and Arrays
209
Array shift(Array a, Point p)
{
for (int i=0; i!=a
...
x += p
...
y += p
...
) and does not implicitly convert to a pointer to an individual element:
ostream& operator<<(ostream& os, Point p)
{
cout << '{' << p[i]
...
y << '}';
}
void print(Point a[],int s) // must specify number of elements
{
for (int i=0; i!=s; ++i)
cout << a[i] << '\n';
}
template
void print(array
{
for (int i=0; i!=a
...
2
...
For example:
struct S1 { int a; };
struct S2 { int a; };
S1
and S2 are two different types, so:
S1 x;
S2 y = x; // error : type mismatch
A struct is also a different type from a type used as a member
...
2
...
8
...
6 Plain Old Data
Sometimes, we want to treat an object as just ‘‘plain old data’’ (a contiguous sequence of bytes in
memory) and not worry about more advanced semantic notions, such as run-time polymorphism
(§3
...
3, §20
...
2), user-defined copy semantics (§3
...
5), etc
...
For example, copying a 100-element array using 100 calls of a copy constructor is unlikely to be as fast as
calling std::memcpy(), which typically simply uses a block-move machine instruction
...
Such ‘‘tricks’’
are not uncommon, and are important, in implementations of containers, such as vector, and in lowlevel I/O routines
...
So, a POD (‘‘Plain Old Data’’) is an object that can be manipulated as ‘‘just data’’ without worrying about complications of class layouts or user-defined semantics for construction, copy, and
move
...
*/ };
struct S6 : S1 { };
struct S7 : S0 { int b; };
struct S8 : S1 { int b; };
struct S9 : S0, S1 {};
// a POD
// a POD
// not a POD (no default constructor)
// a POD (user-defined default constructor)
// a POD
// not a POD (has a virtual function)
// a POD
// a POD
// not a POD (data in both S1 and S8)
// a POD
For us to manipulate an object as ‘‘just data’’ (as a POD), the object must
• not have a complicated layout (e
...
, with a vptr; (§3
...
3, §20
...
2),
• not have nonstandard (user-defined) copy semantics, and
• have a trivial default constructor
...
2
...
Formally (§iso
...
9, §iso
...
A related concept is a trivial type, which is a type with
• a trivial default constructor and
• trivial copy and move operations
Informally, a default constructor is trivial if it does not need to do any work (use =default if you
need to define one §17
...
1)
...
2
...
3
...
3
...
7),
• has multiple access specifiers for non-static data members (§20
...
Basically, a standard layout type is one that has a layout with an obvious equivalent in C and is in
the union of what common C++ Application Binary Interfaces (ABIs) can handle
...
2
...
2, §17
...
Informally, a copy operation is trivial if it can be implemented as a bitwise copy
...
• Its class has a virtual function
...
• Its class has a base or a member that is not trivial
...
Also, an array of trivially
copyable objects is trivially copyable and an array of standard layout objects has standard layout
...
I could do that by only calling mycopy() for
PODs, but that’s error-prone: if I use mycopy() can I rely on a maintainer of the code to remember
never to call mycopy() for non-PODs? Realistically, I cannot
...
Anyway, here is the general
and optimized code:
212
Structures, Unions, and Enumerations
Chapter 8
template
void mycopy(T∗ to, const T∗ from, int count)
{
if (is_pod
memcpy(to,from,count∗sizeof(T));
else
for (int i=0; i!=count; ++i)
to[i]=from[i];
}
The is_pod is a standard-library type property predicate (§35
...
1) defined in
us to ask the question ‘‘Is T a POD?’’ in our code
...
Note that adding or subtracting non-default constructors does not affect layout or performance
(that was not true in C++98)
...
3
...
9) and try to think about their implications to programmers and compiler
writers
...
8
...
7 Fields
It seems extravagant to use a whole byte (a char or a bool) to represent a binary variable – for example, an on/off switch – but a char is the smallest object that can be independently allocated and
addressed in C++ (§7
...
It is possible, however, to bundle several such tiny variables together as
fields in a struct
...
A member is defined to be a field by specifying
the number of bits it is to occupy
...
They do not affect the meaning of
the named fields, but they can be used to make the layout better in some machine-dependent way:
struct PPN {
// R6000 Physical Page Number
unsigned int PFN : 22; // Page Frame Number
int : 3;
// unused
unsigned int CCA : 3;
// Cache Coherency Algorithm
bool nonreachable : 1;
bool dirty : 1;
bool valid : 1;
bool global : 1;
};
This example also illustrates the other main use of fields: to name parts of an externally imposed
layout
...
2
...
It is not possible to take the
address of a field
...
Note that a
bool field really can be represented by a single bit
...
Section 8
...
7
Fields
213
if (p−>dirty) { // contents changed
// copy to disk
p−>dirty = 0;
}
}
Surprisingly, using fields to pack several variables into a single byte does not necessarily save
space
...
Programs have been known to shrink significantly when binary variables were
converted from bit-fields to characters! Furthermore, it is typically much faster to access a char or
an int than to access a field
...
1
...
8
...
Naturally, a union can hold a value for only one
member at a time
...
}
The members s and i can never be used at the same time, so space is wasted
...
s if t==str; use v
...
s;
//
...
3
...
Unions are sometimes misused for ‘‘type conversion
...
For example, the following ‘‘converts’’ an int to an int∗ simply by assuming bitwise
equivalence:
union Fudge {
int i;
int∗ p;
};
int∗ cheat(int i)
{
Fudge a;
a
...
p;
}
// bad use
This is not really a conversion at all
...
Such use of a union is dangerous and nonportable
...
5
...
For example:
int∗ cheat2(int i)
{
return reinterpret_cast
}
// obviously ugly and dangerous
Here, at least the compiler has a chance to warn you if the sizes of objects are different and such
code stands out like the sore thumb it is
...
However, most programs don’t improve much from the use of unions and unions are rather error-prone
...
Section 8
...
1
Unions and Classes
215
8
...
1 Unions and Classes
Many nontrivial unions have a member that is much larger than the most frequently used members
...
This waste
can often be eliminated by using a set of derived classes (§3
...
2, Chapter 20) instead of a union
...
2) which in turn is a kind of a class (Chapter 16)
...
[2] A union cannot have members of reference type
...
[4] If a union has a member with a user-defined constructor, a copy operation, a move operation, or a destructor, then that special function is deleted (§3
...
4, §17
...
4) for that union;
that is, it cannot be used for an object of the union type
...
4
...
[6] A union cannot be used as a base class
...
The latter
is important because the use of unions is often an optimization and we won’t want ‘‘hidden costs’’
imposed to compromise that
...
) from a union with a member that has a constructor (etc
...
For example, since Entry has no member with constructors, destructors, or assignments, we can create and copy Entrys freely
...
For example:
void f2(U x)
{
U u;
U u2 = x;
u
...
m3;
return;
}
// error : which default constructor?
// error : which copy constructor?
// assign to int member
// disaster : read from string member
// error : which destructors are called for x, u, and u2?
It’s illegal to write one member and then read another, but people do that nevertheless (usually by
mistake)
...
It is
216
Structures, Unions, and Enumerations
Chapter 8
fortunate that U won’t compile
...
3
...
If
desired, such a class can also prevent the error of writing one member and then reading another
...
If so, this initializer will
be used for default initialization
...
p == ""
// x2
...
3
...
3):
union,
consider a
class Entry2 { // two alternative representations represented as a union
private:
enum class Tag { number, text };
Tag type; // discriminant
union { // representation
int i;
string s; // string has default constructor, copy operations, and destructor
};
public:
struct Bad_entry { };
// used for exceptions
string name;
˜Entry2();
Entry2& operator=(const Entry2&);
Entry2(const Entry2&);
//
...
};
I’m not a fan of get/set functions, but in this case we really need to perform a nontrivial user-specified action on each access
...
That happens to be my favorite among the many naming conventions
...
3
...
Such a union
is often called a tagged union or a discriminated union
...
˜string();
type = Tag::number;
}
i = n;
}
// explicitly destroy string (§11
...
4)
void Entry2::set_text(const string& ss)
{
if (type==Tag::text)
s = ss;
else {
new(&s) string{ss};
// placement new: explicitly construct string (§11
...
4)
type = Tag::text;
}
}
The use of a union forces us to use otherwise obscure and low-level language facilities (explicit
construction and destruction) to manage the lifetime of the union elements
...
Note that the union in the declaration of Entry2 is not named
...
An anonymous union is an object, not a type, and its members can be accessed without
mentioning an object name
...
Entry2 has a member of a type with a user-defined assignment operator, string, so Entry2’s
assignment operator is deleted (§3
...
4, §17
...
4)
...
Assignment combines the complexities of reading and writing but is otherwise
logically similar to the access functions:
Entry2& Entry2::operator=(const Entry2& e) // necessar y because of the string variant
{
if (type==Tag::text && e
...
s;
// usual string assignment
return ∗this;
}
if (type==Tag::text) s
...
2
...
type) {
case Tag::number:
i = e
...
s); // placement new: explicit construct (§11
...
4)
type = e
...
We need at least a constructor or two to establish the correspondence between the type tag and a value
...
˜string(); // explicit destroy (§11
...
4)
}
8
...
7
...
Some of an enumeration’s possible values are named and called enumerators
...
‘‘An enumeration’’ is colloquially shortened to ‘‘an enum
...
g
...
Section 8
...
1
enum classes
219
8
...
1 enum classes
An enum class is a scoped and strongly typed enumeration
...
*/ }
if (x == red) { /*
...
*/ }
if (x == Traffic_light::red) { /*
...
An enumeration is represented by some integer type and each enumerator by some integer
value
...
The underlying type
must be one of the signed or unsigned integer types (§6
...
4); the default is int
...
Here, we get:
static_cast
static_cast
static_cast
static_cast
Declaring a variable Warning instead of plain
as to the intended use
...
An enumerator can be initialized by a constant expression (§10
...
2
...
For
example:
enum class Printer_flags {
acknowledge=1,
paper_empty=2,
busy=4,
out_of_black=8,
out_of_color=16,
//
};
The values for the Printer_flags enumerators are chosen so that they can be combined by bitwise
operations
...
2
...
1,
Chapter 18)
...
Given these definitions of | and & for Printer_flags, we can write:
void try_to_print(Printer_flags x)
{
if (x&Printer_flags::acknowledge) {
//
...
}
else if (x&(Printer_flags::out_of_black|Printer_flags::out_of_color)) {
// either we are out of black or we are out of color
//
...
}
Section 8
...
1
enum classes
221
I defined operator|() and operator&() to be constexpr functions (§10
...
1
...
For example:
void g(Printer_flags x)
{
switch (x) {
case Printer_flags::acknowledge:
//
...
break;
case Printer_flags::out_of_black:
//
...
break;
case Printer_flags::out_of_black&Printer_flags::out_of_color:
// we are out of black *and* out of color
//
...
}
It is possible to declare an enum class without defining it (§6
...
For example:
enum class Color_code : char;
void foobar(Color_code∗ p);
//
...
The result of such a
conversion is undefined unless the value is within the range of the enumeration’s underlying type
...
222
Structures, Unions, and Enumerations
Chapter 8
Each enumerator has an integer value
...
For example:
int i = static_cast
char c = static_cast
// i becomes 2
// c becomes 8
The notion of a range of values for an enumeration differs from the enumeration notion in the Pascal family of languages
...
g
...
The sizeof an enum class is the sizeof of its underlying type
...
8
...
2 Plain enums
A ‘‘plain enum’’ is roughly what C++ offered before the enum classes were introduced, so you’ll
find them in lots of C and C++98-style code
...
Consider the examples from §8
...
1 with the ‘‘class’’ removed:
enum Traffic_light { red, yellow, green };
enum Warning { green, yellow, orange, red }; // fire alert levels
// error: two definitions of yellow (to the same value)
// error: two definitions of red (to different values)
Warning a1 = 7;
int a2 = green;
int a3 = Warning::green;
Warning a4 = Warning::green;
// error : no int->Warning conversion
// OK: green is in scope and converts to int
// OK: Warning->int conversion
// OK
void f(Traffic_light x)
{
if (x == 9) { /*
...
*/ }
if (x == Warning::red) { /*
...
*/ }
}
// OK (but Traffic_light doesn’t have a 9)
// error : two reds in scope
// OK (Ouch!)
// OK
We were ‘‘lucky’’ that defining red in two plain enumerations in a single scope saved us from hardto-spot errors
...
4
...
*/ }
if (x == Warning::red) { /*
...
*/ }
}
223
// OK (ouch!)
// OK (ouch!)
// error : red is not a Traffic_light value
The compiler accepts the x==red, which is almost certainly a bug
...
You can specify the underlying type of a plain enumeration, just as you can for enum classes
...
For example:
enum Traffic_light : char { tl_red, tl_yellow, tl_green };
// underlying type is char
enum Color_code : char;
// declaration
void foobar(Color_code∗ p); // use of declaration
//
...
If there are negative enumerators, the range is [-2 :2 -1]
...
For example:
enum E1 { dark, light };
// range 0:1
enum E2 { a = 3, b = 9 };
// range 0:15
enum E3 { min = −10, max = 1000000 }; // range -1048576:1048575
The rule for explicit conversion of an integer to a plain enum is the same as for the class enum
except that when there is no explicit underlying type, the result of such a conversion is undefined
unless the value is within the range of the enumeration
...
The
sizeof an enumeration is the sizeof its underlying type
...
For example, sizeof(e1) could be 1 or
maybe 4 but not 8 on a machine where sizeof(int)==4
...
4
...
For example:
enum { arrow_up=1, arrow_down, arrow_sideways };
We use that when all we need is a set of integer constants, rather than a type to use for variables
...
5 Advice
[1]
[2]
[3]
[4]
[5]
[6]
[7]
When compactness of data is important, lay out structure data members with larger members
before smaller ones; §8
...
1
...
2
...
Don’t naively try to optimize memory consumption by packing several values into a single
byte; §8
...
7
...
3
...
4
...
4
...
4
...
9
Statements
A programmer is a machine
for turning caffeine into code
...
1 Introduction
C++ offers a conventional and flexible set of statements
...
Note that a declaration is a statement and
that an expression becomes a statement when you add a semicolon at its end
...
Instead, statements are used to specify
the order of execution
...
A compiler may reorder code
to improve performance as long as the result is identical to that of the simple order of execution
...
2 Statement Summary
Here is a summary of C++ statements:
statement:
declaration
expressionopt ;
{ statement-listopt }
try { statement-listopt } handler-list
case constant-expression :
default : statement
break ;
continue ;
return
statement
expressionopt ;
identifier ;
identifier : statement
goto
selection-statement
iteration-statement
selection-statement:
if ( condition ) statement
if ( condition ) statement else statement
switch ( condition ) statement
iteration-statement:
while ( condition ) statement
do statement while ( expression ) ;
for ( for-init-statement conditionopt ; expressionopt ) statement
for ( for-init-declaration : expression ) statement
statement-list:
statement statement-listopt
condition:
expression
type-specifier declarator = expression
type-specifier declarator { expression }
handler-list:
handler handler-listopt
handler:
catch (
exception-declaration ) { statement-listopt }
A semicolon is by itself a statement, the empty statement
...
2
Statement Summary
227
A (possibly empty) sequence of statements within ‘‘curly braces’’ (i
...
, { and }) is called a block
or a compound statement
...
3
...
A declaration is a statement and there is no assignment statement or procedure-call statement;
assignments and function calls are expressions
...
Note that both end
with a semicolon
...
The statements for handling exceptions, try-blocks, are described in §13
...
9
...
Unless a variable is declared static, its initializer is executed whenever
the thread of control passes through the declaration (see also §6
...
2)
...
4
...
5
...
There is rarely a reason to introduce a variable before there is a value for it to hold
...
size()<=i)
error("bad index");
string s = v[i];
if (s == p) {
//
...
}
The ability to place declarations after executable code is essential for many constants and for single-assignment styles of programming where a value of an object is not changed after initialization
...
For example:
void use()
{
string s1;
s1 = "The best is the enemy of the good
...
}
This requests a default initialization (to the empty string) followed by an assignment
...
Input variables are among the few reasonable examples of that:
void input()
{
int buf[max];
int count = 0;
for (int i; cin>>i;) {
if (i<0) error("unexpected negative value");
if (count==max) error("buffer overflow");
buf[count++] = i;
}
//
...
Often,
push_back() (§3
...
1
...
6, §31
...
6) provides a better solution to such examples
...
4 Selection Statements
A value can be tested by either an if-statement or a switch-statement:
condition ) statement
condition ) statement else statement
switch ( condition ) statement
if (
if (
A condition is either an expression or a declaration (§9
...
3)
...
4
...
If a condition evaluates to something different
from a Boolean, it is – if possible – implicitly converted to a bool
...
For example, if x is an integer, then
if (x) //
...
For a pointer p,
if (p) //
...
Note that a ‘‘plain’’ enum can be implicitly converted to an integer and then to a
enum class cannot (§8
...
1)
...
4
...
if (y)
//
...
}
// OK
// error: no conversion to bool
// OK
The logical operators
&& ||
!
are most commonly used in conditions
...
For example,
if (p && 1
count) //
...
For choosing between two alternatives each of which produces a value, a conditional expression
(§11
...
3) is a more direct expression of intent than an if-statement
...
In particular, it cannot be used
on another branch of an if-statement
...
}
else {
++x; // error: x is not in scope
}
++x;
// error: x is not in scope
}
A branch of an if-statement cannot be just a declaration
...
2)
...
4
...
The expression in the case
labels must be a constant expression of integral or enumeration type
...
For example:
void f(int i)
{
switch (i) {
case 2
...
case 2:
//
...
};
A switch-statement can alternatively be written as a set of if-statements
...
This makes the switch-statement
easier to read for nontrivial examples
...
Instead, a jump table can be used
...
4
...
Consider:
switch (val) {
// beware
case 1:
cout << "case 1\n";
case 2:
cout << "case 2\n";
default:
cout << "default: case not found\n";
}
Invoked with val==1, the output will greatly surprise the uninitiated:
case 1
case 2
default: case not found
It is a good idea to comment the (rare) cases in which a fall-through is intentional so that an uncommented fall-through can be assumed to be an error
...
}
A break is the most common way of terminating a case, but a return is often useful (§10
...
1)
...
One use is for the default to handle the most common case
...
However, there is one case where a default should not be used: if a switch is intended
to have one case for each enumerator of an enumeration
...
For example, this is almost certainly an error:
enum class Vessel { cup, glass, goblet, chalice };
void problematic(Vessel v)
{
switch (v) {
case Vessel::cup:
case Vessel::glass:
case Vessel::goblet:
}
}
/*
...
*/
/*
...
Testing for an ‘‘impossible’’ enumerator value is best done separately
...
4
...
1 Declarations in Cases
It is possible, and common, to declare variables within the block of a switch-statement
...
For example:
void f(int i)
{
switch (i) {
case 0:
int x;
int y = 3;
string s;
case 1:
++x;
++y;
s = "nasty!";
}
}
// uninitialized
// error: declaration can be bypassed (explicitly initialized)
// error: declaration can be bypassed (implicitly initialized)
// error: use of uninitialized object
Here, if i==1, the thread of execution would bypass the initializations of y and s, so f() will not compile
...
However, its use is an error: we read an uninitialized variable
...
As
usual, avoid uninitialized variables (§6
...
5
...
If we need a variable within a switch-statement, we can limit its scope by enclosing its declaration and its use in a block
...
2
...
9
...
3 Declarations in Conditions
To avoid accidental misuse of a variable, it is usually a good idea to introduce the variable into the
smallest scope possible
...
That way, one cannot get into trouble by using the variable
before its initial value is assigned
...
Consider:
if (double d = prim(true)) {
left /= d;
break;
}
Here, d is declared and initialized and the value of d after initialization is tested as the value of the
condition
...
For example, had there been an else-branch to the if-statement, d would be in
scope on both branches
...
4
...
However, this opens
the scope (literally) for the use of d before its initialization or after its intended useful life:
double d;
//
...
if (d = prim(true)) {
left /= d;
break;
}
//
...
0; // two unrelated uses of d
In addition to the logical benefits of declaring variables in conditions, doing so also yields the most
compact source code
...
9
...
Note that both end
with a semicolon
...
More complicated loops can be expressed as an algorithm plus a lambda expression (§11
...
2)
...
5
...
For example:
int sum(vector
{
int s = 0;
for (int x : v)
s+=x;
return s;
}
234
Statements
Chapter 9
The for (int x : v) can be read as ‘‘for each element x in the range v’’ or just ‘‘for each x in v
...
The scope of the variable naming the element (here, x) is the for-statement
...
begin() and v
...
5):
[1] the compiler first looks for members begin and end and tries to use those
...
g
...
[2] Otherwise, the compiler looks for a begin/end member pair in the enclosing scope
...
g
...
The compiler uses v and v+N as begin(v) and end(v) for a built-in array T v[N]
...
For
sequences of our own design, we can define begin() and end() in the same way as it is done for standard-library containers (§4
...
5)
...
For example, we can increment each element of a vector like this:
void incr(vector
{
for (int& x : v)
++x;
}
References are also appropriate for elements that might be large, so that copying them to the element value could be costly
...
For example, using it you can’t touch
two elements at the same time and can’t effectively traverse two ranges simultaneously
...
Section 9
...
2
for
Statements
235
9
...
2 for Statements
There is also a more general for-statement allowing greater control of the iteration
...
For example:
void f(int v[], int max)
{
for (int i = 0; i!=max; ++i)
v[i] = i∗i;
}
This is equivalent to
void f(int v[], int max)
{
int i = 0;
// introduce loop variable
while (i!=max) {
// test termination condition
v[i] = i∗i; // execute the loop body
++i;
// increment loop variable
}
}
A variable can be declared in the initializer part of a for-statement
...
It is not always obvious what is the right type to use for a controlled variable in a for loop, so
auto often comes in handy:
for (auto p = begin(c); c!=end(c); ++p) {
//
...
}
If the final value of an index needs to be known after exit from a for-loop, the index variable must
be declared outside the for-loop (e
...
, see §9
...
If no initialization is needed, the initializing statement can be empty
...
If the loop isn’t of the simple ‘‘introduce a loop variable, test the condition, update the loop variable’’ variety, it is often better
expressed as a while-statement
...
push_back(s);
Here, the reading and testing for termination and combined in cin>>s, so we don’t need an explicit
loop variable
...
A for-statement is also useful for expressing a loop without an explicit termination condition:
for (;;) { // ‘‘forever’’
//
...
}
// ‘‘forever’’
9
...
3 while Statements
A while-statement executes its controlled statement until its condition becomes false
...
A for-statement (§9
...
2) is easily rewritten into an equivalent while-statement and vice versa
...
5
...
For
// i must be positive
This might be called like this: print_backwards(s,strlen(s)); but it is all too easy to make a horrible
mistake
...
The reason is that its
body is always executed once before the condition is evaluated
...
More often
than I would have guessed, I have found that condition not to hold as expected either when the program was first written and tested or later after the code preceding it has been modified
...
’’ Consequently, I recommend avoiding do-statements
...
5
...
1
...
6), throw
(§13
...
4
...
A break ‘‘breaks out of’’ the
Section 9
...
5
Loop Exit
237
nearest enclosing switch-statement (§9
...
2) or iteration-statement
...
if (c == '\n') break;
//
...
’’ Unless it warps the logic of
a loop (e
...
, requires the introduction of an extra varible), it is usually better to have the complete
exit condition as the condition of a while-statement or a for-statement
...
A continue skips the rest of the body of an iteration-statement
...
size(); ++i) {
if (!prime(v[i]) continue;
return v[i];
}
}
After a continue, the increment part of the loop (if any) is executed, followed by the loop condition
(if any)
...
size(); ++i) {
if (!prime(v[i]) {
return v[i];
}
}
}
9
...
238
Statements
Chapter 9
The scope of a label is the function it is in (§6
...
4)
...
The only restriction is that you cannot jump past an initializer or into
an exception handler (§13
...
One of the few sensible uses of goto in ordinary code is to break out from a nested loop or
switch-statement (a break breaks out of only the innermost enclosing loop or switch-statement)
...
found:
// nm[i][j] == a
}
Note that this goto just jumps forward to exit its loop
...
That makes it the least troublesome and least confusing use of a goto
...
7 Comments and Indentation
Judicious use of comments and consistent use of indentation can make the task of reading and
understanding a program much more pleasant
...
I see no fundamental reason to prefer one over another (although, like most programmers, I
have my preferences, and this book reflects them)
...
Comments can be misused in ways that seriously affect the readability of a program
...
Most programs contain comments that are incomprehensible, ambiguous, and just plain wrong
...
If something can be stated in the language itself, it should be, and not just mentioned in a comment
...
7
Comments and Indentation
239
// don’t use function "weird()"
// function "f(int
...
Once something has been stated clearly in the language, it should not be mentioned a second
time in a comment
...
They increase the amount of text the reader has
to look at, they often obscure the structure of the program, and they may be wrong
...
This is one of the many ways a program in a textbook differs from a real program
...
Preferably, a comment is expressed
at a suitably high level of abstraction so that it is easy for a human to understand without delving
into minute details
...
• A comment for each class, template, and namespace
• A comment for each nontrivial function stating its purpose, the algorithm used (unless it is
obvious), and maybe something about the assumptions it makes about its environment
• A comment for each global and namespace variable and constant
• A few comments where the code is nonobvious and/or nonportable
• Very little else
For example:
//
tbl
...
/*
Gaussian elimination with partial pivoting
...
" pg 411
...
// Revised to handle invalid dates
...
Writing
good comments can be as difficult as writing the program itself
...
240
Statements
Chapter 9
Note that /∗ ∗/ style comments do not nest
...
9
...
3, §9
...
3, §9
...
2
...
4
...
Prefer a range-for-statement to a for-statement when there is a choice; §9
...
1
...
5
...
Prefer a while-statement to a for-statement when there is no obvious loop variable; §9
...
3
...
5
...
6
...
7
...
7
...
7
...
7
...
– apologies to Richard Feynman
•
•
•
•
•
•
Introduction
A Desk Calculator
The Parser; Input; Low-Level Input; Error Handling; The Driver; Headers; Command-Line
Arguments; A Note on Style
Operator Summary
Results; Order of Evaluation; Operator Precedence; Temporary Objects
Constant Expressions
Symbolic Constants; consts in Constant Expressions; Literal Types; Reference Arguments;
Address Constant Expressions
Implicit Type Conversion
Promotions; Conversions; Usual Arithmetic Conversions
Advice
10
...
In C++, an assignment is an expression, a function call is an expression, the construction of an object is an expression, and so are many other
operations that go beyond conventional arithmetic expression evaluation
...
’’ Next, the complete set of operators is listed and their meaning for builtin types is briefly outlined
...
242
Expressions
Chapter 10
10
...
The user can also define variables
...
5
area = pi ∗ r ∗ r
(pi is predefined) the calculator program will write
2
...
635
where 2
...
635 is the result of the second
...
Actually, it is a miniature compiler in which the parser does the syntactic analysis, the input
function handles input and lexical analysis, the symbol table holds permanent information, and the
driver handles initialization, output, and errors
...
10
...
1 The Parser
Here is a grammar for the language accepted by the calculator:
program:
end
expr_list end
// end is end-of-input
expr_list:
expression print
expression print expr_list
// print is newline or semicolon
expression:
expression + term
expression − term
term
term:
term / primary
term ∗ primary
primary
primary:
number
name
name = expression
− primary
( expression )
// number is a floating-point literal
// name is an identifier
Section 10
...
1
The Parser
243
In other words, a program is a sequence of expressions separated by semicolons
...
Names need not be declared before use
...
In a language such as C++, in which function calls are relatively cheap, it is also
efficient
...
Terminal symbols (for example, end, number, +, and −) are recognized by a lexical analyzer and nonterminal symbols are recognized by the syntax analyzer functions, expr(), term(), and prim()
...
For input, the parser uses a Token_stream that encapsulates the reading of characters and their
composition into Tokens
...
45, into Tokens
...
45}, where the
123
...
The main parts of the parser need only to know
the name of the Token_stream, ts, and how to get Tokens from it
...
get()
...
current()
...
We’ll see that
they can come directly from a user typing to cin, from a program command line, or from any other
input stream (§10
...
7)
...
This works as long as no character used as input has a value used
as an enumerator – and no current character set I know of has a printing character with a singledigit integer value
...
};
The implementation is presented in §10
...
2
...
2
...
Each parser function evaluates ‘‘its’’
244
Expressions
Chapter 10
expression and returns the value
...
It consists
of a single loop that looks for terms to add or subtract:
double expr(bool get)
{
double left = term(get);
// add and subtract
for (;;) {
// ‘‘forever’’
switch (ts
...
kind) {
case Kind::plus:
left += term(true);
break;
case Kind::minus:
left −= term(true);
break;
default:
return left;
}
}
}
This function really does not do much itself
...
The switch-statement (§2
...
4, §9
...
2) tests the value of its condition, which is supplied in parentheses after the switch keyword, against a set of constants
...
If the value tested does not match any case label, the default is chosen
...
Note that an expression such as 2−3+4 is evaluated as (2−3)+4, as specified in the grammar
...
5); while(true) is an alternative
...
The operators += and −= are used to handle the addition and subtraction; left=left+term(true) and
left=left−term(true) could have been used without changing the meaning of the program
...
Each assignment operator is a separate lexical token, so a + = 1; is a syntax error because
of the space between the + and the =
...
3 summarizes the operators
and their meanings
...
Section 10
...
1
The Parser
245
The function term() handles multiplication and division in the same way expr() handles addition
and subtraction:
double term(bool get)
{
double left = prim(get);
// multiply and divide
for (;;) {
switch (ts
...
kind) {
case Kind::mul:
left ∗= prim(true);
break;
case Kind::div:
if (auto d = prim(true)) {
left /= d;
break;
}
return error("divide by 0");
default:
return left;
}
}
}
The result of dividing by zero is undefined and usually disastrous
...
The function error() is described in §10
...
4
...
The scope of a name introduced in a condition is the statement controlled by that condition,
and the resulting value is the value of the condition (§9
...
3)
...
The function prim() handling a primary is much like expr() and term(), except that because we are
getting lower in the call hierarchy a bit of real work is being done and no loop is necessary:
double prim(bool get)
// handle primaries
{
if (get) ts
...
current()
...
current()
...
get();
return v;
}
case Kind::name:
{
double& v = table[ts
...
string_value];
if (ts
...
kind == Kind::assign) v = expr(true);
return v;
}
// find the corresponding
// ’=’ seen: assignment
246
Expressions
Chapter 10
case Kind::minus:
// unar y minus
return −prim(true);
case Kind::lp:
{
auto e = expr(true);
if (ts
...
kind != Kind::rp) return error("')' expected");
ts
...
Similarly, when a Token that is a name (however defined; see §10
...
2 and
§10
...
3) is seen, its value is placed in its string_value
...
The reason is that it must do that in some cases (e
...
, to see if a name is assigned to), so for consistency it must do it in all cases
...
get()
...
current()
...
get())
...
In both cases, the symbol table is consulted
...
4
...
4
...
For example, if the user enters
double
corresponding to the
radius = 6378
...
expr() calculates the value to be assigned
...
388;
The reference v is used to hold on to the double associated with radius while expr() calculates the
value 6378
...
Chapter 14 and Chapter 15 discuss how to organize a program as a set of modules
...
The exception is expr(), which calls term(), which calls
prim(), which in turn calls expr()
...
A declaration
double expr(bool);
before the definition of prim() will do nicely
...
2
...
2
...
To communicate with a person, the program
must cope with that person’s whims, conventions, and seemingly random errors
...
The task of a low-level input routine is to read characters and compose higher-level tokens
from them
...
Here, low-level input
is done by ts
...
Writing a low-level input routine need not be an everyday task
...
First we need to see the complete definition of Token_stream:
class Token_stream {
public:
Token_stream(istream& s) : ip{&s}, owns{false} { }
Token_stream(istream∗ p) : ip{p}, owns{true} { }
˜Token_stream() { close(); }
Token get();
Token& current();
// read and return next token
// most recently read token
void set_input(istream& s) { close(); ip = &s; owns=false; }
void set_input(istream∗ p) { close(); ip = p; owns = true; }
private:
void close() { if (owns) delete ip; }
istream∗ ip;
bool owns;
Token ct {Kind::end} ;
// pointer to an input stream
// does the Token_stream own the istream?
// current token
};
We initialize a Token_stream with an input stream (§4
...
2, Chapter 38) from which it gets its characters
...
2
...
2,
§11
...
This may be a bit
elaborate for this simple program, but it is a useful and general technique for classes that hold a
pointer to a resource requiring destruction
...
I gave ct a default value because it seemed sloppy not to
...
I chose Kind::end as the initial value for ct so
that a program that misuses current() will not get a value that wasn’t on the input stream
...
First, I provide a deceptively simple version that
imposes a burden on the user
...
The idea for get() is to read a character, use that character to decide what kind of token
needs to be composed, read more characters when needed, and then return a Token representing the
characters read
...
) and leaves the value
of ch unchanged if the input operation failed
...
Assignment is an operator, and the result of the assignment is the value of the variable assigned
to
...
Having a single statement rather than two is useful in maintenance
...
Note also how the {}-list notation (§3
...
1
...
3) is used on the right-hand side of an assignment
...
I could have written that return-statement as:
ct
...
The {Kind::end} is equivalent to {Kind::end,0,0}
...
Neither is the
case here, but in general dealing with complete objects is clearer and less error-prone than manipulating data members individually
...
Consider some of the cases separately before considering the complete function
...
5
...
4
...
Numbers are handled like this:
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
case '
...
2
...
) back into the input stream
∗ip >> ct
...
kind=Kind::number;
return ct;
Stacking case labels horizontally rather than vertically is generally not a good idea because this
arrangement is harder to read
...
Because operator >> is already defined for reading floating-point values into a double, the code is trivial
...
Then, the floating-point value can be read
into ct
...
If the token is not the end of input, an operator, a punctuation character, or a number, it must be
a name
...
string_value;
// read the string into ct
ct
...
The simple-minded, but reasonably effective way to deal
with an error is the write call an error() function and then return a print token if error() returns:
error("bad token");
return ct={Kind::print};
The standard-library function isalpha() (§36
...
1) is used to avoid listing every character as a separate case label
...
Consequently, a user must terminate a name by a space before an operator using the name
as an operand
...
2
...
Here, finally, is the complete input function:
Token Token_stream::get()
{
char ch = 0;
∗ip>>ch;
switch (ch) {
case 0:
return ct={Kind::end};
// assign and return
case ';': // end of expression; print
case '∗':
case '/':
case '+':
case '−':
case '(':
case ')':
case '=':
return ct=={static_cast
250
Expressions
Chapter 10
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
case '
...
) back into the input stream
∗ip >> ct
...
kind=Kind::number;
return ct;
default:
// name, name =, or error
if (isalpha(ch)) {
ip−>putback(ch);
// put the first character back into the input stream
∗ip>>ct
...
kind=Kind::name;
return ct;
}
error("bad token");
return ct={Kind::print};
}
}
The conversion of an operator to its Token value is trivial because the
defined as the integer value of the operator (§10
...
1)
...
2
...
It is tedious to remember to
add a semicolon after an expression in order to get its value printed, and having a name terminated
by whitespace only is a real nuisance
...
To get what we (usually) want, we would have to add
whitespace after x: x =7
...
First, we’ll make a newline equivalent to the semicolon used to mark the end-of-expression:
Token Token_stream::get()
{
char ch;
do { // skip whitespace except ’\n’
if (!ip−>get(ch)) return ct={Kind::end};
} while (ch!='\n' && isspace(ch));
switch (ch) {
case ';':
case '\n':
return ct={Kind::print};
Here, I use a do-statement; it is equivalent to a while-statement except that the controlled statement
is always executed at least once
...
By default, get() does not skip whitespace the way >> does
...
The operator ! (not) is used because get() returns true in case of success
...
2
...
2
...
The test is
implemented as a table lookup, so using isspace() is much faster than testing for the individual
whitespace characters
...
After whitespace has been skipped, the next character is used to determine what kind of lexical
token is coming
...
Constructing programs so that improvements can be implemented through local modifications only is an important design aim
...
It
would be for very long strings, but all modern string implementations provide the ‘‘small string
optimization’’ (§19
...
3)
...
In particular,
using a short string doesn’t require any use of free store
...
10
...
4 Error Handling
It is always important to detect and report errors
...
The error() function simply counts the errors, writes out an error message,
and returns:
int no_of_errors;
double error(const string& s)
{
no_of_errors++;
cerr << "error: " << s << '\n';
return 1;
}
The stream cerr is an unbuffered output stream usually used to report errors (§38
...
The reason for returning a value is that errors typically occur in the middle of the evaluation of
an expression, so we should either abort that evaluation entirely or return a value that is unlikely to
cause subsequent errors
...
Had Token_stream::get()
252
Expressions
Chapter 10
kept track of the line numbers, error() could have informed the user approximately where the error
occurred
...
A more stylized and general error-handling strategy would separate error detection from error
recovery
...
4
...
1, Chapter 13), but what we have
here is quite suitable for a 180-line calculator
...
2
...
I decided on two
functions: main() to do setup and error reporting and calculate() to handle the actual calculation:
Token_stream ts {cin};
// use input from cin
void calculate()
{
for (;;) {
ts
...
current()
...
current()
...
1415926535897932385;
table["e"] = 2
...
2
...
Returning the number of errors accomplishes this nicely
...
The primary task of the main loop (in calculate()) is to read expressions and write out the
answer
...
get() to read a token on which to work
...
get() encounters an input
error or an end-of-file
...
5)
...
A continue-statement is equivalent to going to the very end of a loop
...
2
...
2
...
Therefore, appropriate headers must be #included to
complete the program:
#include
#include
// strings
#include