Beginning C++20 - From Novice to Professional (true pdf) [6 ed.] 9781484258835, 9781484258842

Begin your programming journey with C++ , starting with the basics and progressing through step-by-step examples that wi

12,909 3,008 11MB

English Pages [843] Year 2020

Report DMCA / Copyright

DOWNLOAD FILE

Polecaj historie

Beginning C++20 - From Novice to Professional (true pdf) [6 ed.]
 9781484258835, 9781484258842

Table of contents :
Table of Contents
About the Authors
About the Technical Reviewer
Introduction
Chapter 1: Basic Ideas
Modern C++
Standard Libraries
C++ Program Concepts
Source Files
Comments and Whitespace
Standard Library Modules
Functions
Statements
Data Input and Output
return Statements
Namespaces
Names and Keywords
Classes and Objects
Templates
Code Appearance and Programming Style
Creating an Executable
Procedural and Object-Oriented Programming
Representing Numbers
Binary Numbers
Hexadecimal Numbers
Negative Binary Numbers
Octal Values
Big-Endian and Little-Endian Systems
Floating-Point Numbers
Representing Characters
ASCII Codes
UCS and Unicode
C++ Source Characters
Escape Sequences
Summary
Chapter 2: Introducing Fundamental Types of Data
Variables, Data, and Data Types
Defining Integer Variables
Signed Integer Types
Unsigned Integer Types
Zero Initialization
Defining Variables with Fixed Values
Integer Literals
Decimal Integer Literals
Hexadecimal Literals
Octal Literals
Binary Literals
Calculations with Integers
Compound Arithmetic Expressions
Assignment Operations
The op= Assignment Operators
The sizeof Operator
Incrementing and Decrementing Integers
Postfix Increment and Decrement Operations
Defining Floating-Point Variables
Floating-Point Literals
Floating-Point Calculations
Mathematical Constants
Mathematical Functions
Invalid Floating-Point Results
Pitfalls
Mixed Expressions and Type Conversion
Explicit Type Conversion
Old-Style Casts
Formatting Strings
Formatting Stream Output
String Formatting with std::format()
Format Specifiers
Formatting Tabular Data
Formatting Numbers
Argument Indexes
Finding the Limits
Finding Other Properties of Fundamental Types
Working with Character Variables
Working with Unicode Characters
The auto Keyword
Summary
Chapter 3: Working with Fundamental Data Types
Operator Precedence and Associativity
Bitwise Operators
The Bitwise Shift Operators
Shifting Signed Integers
Logical Operations on Bit Patterns
Using the Bitwise AND
Using the Bitwise OR
Using the Bitwise Complement Operator
Using the Bitwise Exclusive OR
Using the Bitwise Operators: An Example
The Lifetime of a Variable
Global Variables
Enumerated Data Types
Aliases for Data Types
Summary
Chapter 4: Making Decisions
Comparing Data Values
Applying the Comparison Operators
Comparing Floating-Point Values
The Spaceship Operator
Comparison Categories
Named Comparison Functions
The if Statement
Nested if Statements
Character Classification and Conversion
The if-else Statement
Nested if-else Statements
Understanding Nested ifs
Logical Operators
Logical AND
Logical OR
Logical Negation
Combining Logical Operators
Logical Operators on Integer Operands
Logical Operators vs. Bitwise Operators
Short-Circuit Evaluation
Logical XOR
The Conditional Operator
The switch Statement
Fallthrough
Statement Blocks and Variable Scope
Initialization Statements
Summary
Chapter 5: Arrays and Loops
Arrays
Using an Array
Understanding Loops
The for Loop
Avoiding Magic Numbers
Defining the Array Size with the Braced Initializer
Determining the Size of an Array
Controlling a for Loop with Floating-Point Values
More Complex for Loop Control Expressions
The Comma Operator
The Range-Based for Loop
The while Loop
The do-while Loop
Nested Loops
Skipping Loop Iterations
Breaking Out of a Loop
Indefinite Loops
Controlling a for Loop with Unsigned Integers
Arrays of Characters
Multidimensional Arrays
Initializing Multidimensional Arrays
Setting Dimensions by Default
Multidimensional Character Arrays
Allocating an Array at Runtime
Alternatives to Using an Array
Using array Containers
Accessing Individual Elements
Operations on arrays As a Whole
Conclusion and Example
Using std::vector Containers
Deleting Elements
Example and Conclusion
Summary
Chapter 6: Pointers and References
What Is a Pointer?
The Address-Of Operator
The Indirection Operator
Why Use Pointers?
Pointers to Type char
Arrays of Pointers
Constant Pointers and Pointers to Constants
Pointers and Arrays
Pointer Arithmetic
The Difference Between Pointers
Comparing Pointers
Using Pointer Notation with an Array Name
Dynamic Memory Allocation
The Stack and the Free Store
Using the new and delete Operators
Dynamic Allocation of Arrays
Multidimensional Arrays
Member Selection Through a Pointer
Hazards of Dynamic Memory Allocation
Dangling Pointers and Multiple Deallocations
Allocation/Deallocation Mismatch
Memory Leaks
Fragmentation of the Free Store
Golden Rule of Dynamic Memory Allocation
Raw Pointers and Smart Pointers
Using unique_ptr Pointers
Using shared_ptr Pointers
Understanding References
Defining References
Using a Reference Variable in a Range-Based for Loop
Summary
Chapter 7: Working with Strings
A Better Class of String
Defining string Objects
Operations with String Objects
Concatenating Strings
Concatenating Strings and Characters
Concatenating Strings and Numbers
Accessing Characters in a String
Accessing Substrings
Comparing Strings
Three-Way Comparisons
Comparing Substrings Using compare()
Comparing Substrings Using substr()
Checking the Start or End of a String
Searching Strings
Searching Within Substrings
Searching for Any of a Set of Characters
Searching a String Backward
Modifying a String
Inserting a String
Replacing a Substring
Removing Characters from a String
std::string vs. std::vector
Converting Strings into Numbers
Strings of International Characters
Strings of wchar_t Characters
Objects That Contain Unicode Strings
Raw String Literals
Summary
Chapter 8: Defining Functions
Segmenting Your Programs
Functions in Classes
Characteristics of a Function
Defining Functions
The Function Body
Return Values
How the return Statement Works
Function Declarations
Function Prototypes
Passing Arguments to a Function
Pass-by-Value
Passing a Pointer to a Function
Passing an Array to a Function
const Pointer Parameters
Passing a Multidimensional Array to a Function
Pass-by-Reference
References vs. Pointers
Input vs. Output Parameters
Passing Arrays by Reference
References and Implicit Conversions
Default Argument Values
Multiple Default Parameter Values
Arguments to main()
Returning Values from a Function
Returning a Pointer
Returning a Reference
Returning vs. Output Parameters
Return Type Deduction
Static Variables
Function Overloading
Overloading and Pointer Parameters
Overloading and Reference Parameters
Overloading and const Parameters
Overloading with const Pointer Parameters
Overloading and Reference-to-const Parameters
Overloading and Default Argument Values
Recursion
Basic Examples of Recursion
Recursive Algorithms
The Quicksort Algorithm
The main() Function
The extract_words() Function
The swap() Function
The sort() Functions
The max_word_length() Function
The show_words() Function
Summary
Chapter 9: Vocabulary Types
Working with Optional Values
std::optional
String Views: The New Reference-to-const-string
Using String View Function Parameters
A Proper Motivation
Spans: The New Reference-to-vector or -array
Spans vs. Views
Spans of const Elements
Fixed-Size Spans
Summary
Chapter 10: Function Templates
Function Templates
Creating Instances of a Function Template
Template Type Parameters
Explicit Template Arguments
Function Template Specialization
Function Templates and Overloading
Function Templates with Multiple Parameters
Return Type Deduction in Templates
decltype(auto)
Default Values for Template Parameters
Non-Type Template Parameters
Templates for Functions with Fixed-Size Array Arguments
Abbreviated Function Templates
Limitations to Abbreviated Function Templates
Summary
Chapter 11: Modules and Namespaces
Modules
Your First Module
Export Blocks
Separating Interface from Implementation
Module Implementation Files
Limitations to Implementation Files
Implicit Imports in Implementation Files
Reachability vs. Visibility
Exporting Import Declarations
Managing Larger Modules
Simulating Submodules
Module Partitions
Module Implementation Partitions
Module Interface Partitions
Global Module Fragments
Namespaces
The Global Namespace
Defining a Namespace
Nested Namespaces
Namespaces and Modules
Organizing Larger Namespaces and Modules
Functions and Namespaces
Using Directives and Declarations
Namespace Aliases
Summary
Chapter 12: Defining Your Own Data Types
Classes and Object-Oriented Programming
Encapsulation
Data Hiding
Inheritance
Polymorphism
Terminology
Defining a Class
Creating Objects of a Class
Constructors
Default Constructors
Defining a Class Constructor
Using the default Keyword
Defining Functions Outside the Class
Default Arguments for Constructor Parameters
Using a Member Initializer List
Using the explicit Keyword
Delegating Constructors
The Copy Constructor
Implementing the Copy Constructor
Deleting the Copy Constructor
Defining Classes in Modules
Accessing Private Class Members
The this Pointer
Returning this from a Function
const Objects and const Member Functions
const Member Functions
const Correctness
Overloading on const
Casting Away const
Using the mutable Keyword
Friends
The Friend Functions of a Class
Friend Classes
Arrays of Class Objects
The Size of a Class Object
Static Members of a Class
Static Member Variables
Accessing Static Member Variables
Static Constants
Static Member Variables of the Class Type Itself
Static Member Functions
Destructors
Using Pointers as Class Members
The Truckload Example
The SharedBox Type Alias
Defining the Package Class
Defining the Truckload Class
Traversing the Boxes Contained in a Truckload
Adding and Removing Boxes
Generating Random Boxes
Putting It All Together
Nested Classes
Nested Classes with Public Access
A Better Mechanism for Traversing a Truckload: Iterators
Summary
Chapter 13: Operator Overloading
Implementing Operators for a Class
Operator Overloading
Implementing an Overloaded Operator
Nonmember Operator Functions
Implementing Full Support for an Operator
Operators That Can Be Overloaded
Restrictions and Key Guideline
Operator Function Idioms
Supporting All Comparison Operators
Defaulting Comparison Operators
Overloading the 

Citation preview

Beginning C++20 From Novice to Professional — Sixth Edition — Ivor Horton Peter Van Weert

Beginning C++20 From Novice to Professional Sixth Edition

Ivor Horton Peter Van Weert

Beginning C++20: From Novice to Professional Ivor Horton Stratford-upon-Avon, Warwickshire, UK ISBN-13 (pbk): 978-1-4842-5883-5 https://doi.org/10.1007/978-1-4842-5884-2

Peter Van Weert Kessel-Lo, Belgium ISBN-13 (electronic): 978-1-4842-5884-2

Copyright © 2020 by Ivor Horton and Peter Van Weert This work is subject to copyright. All rights are reserved by the Publisher, whether the whole or part of the material is concerned, specifically the rights of translation, reprinting, reuse of illustrations, recitation, broadcasting, reproduction on microfilms or in any other physical way, and transmission or information storage and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now known or hereafter developed. Trademarked names, logos, and images may appear in this book. Rather than use a trademark symbol with every occurrence of a trademarked name, logo, or image we use the names, logos, and images only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark. The use in this publication of trade names, trademarks, service marks, and similar terms, even if they are not identified as such, is not to be taken as an expression of opinion as to whether or not they are subject to proprietary rights. While the advice and information in this book are believed to be true and accurate at the date of publication, neither the authors nor the editors nor the publisher can accept any legal responsibility for any errors or omissions that may be made. The publisher makes no warranty, express or implied, with respect to the material contained herein. Managing Director, Apress Media LLC: Welmoed Spahr Acquisitions Editor: Steve Anglin Development Editor: Matthew Moodie Coordinating Editor: Mark Powers Cover designed by eStudioCalamar Cover image designed by Casey Horner on Unsplash (www.unsplash.com) Distributed to the book trade worldwide by Apress Media, LLC, 1 New York Plaza, New York, NY 10004, U.S.A. Phone 1-800-SPRINGER, fax (201) 348-4505, e-mail [email protected], or visit www.springeronline.com. Apress Media, LLC is a California LLC and the sole member (owner) is Springer Science + Business Media Finance Inc (SSBM Finance Inc). SSBM Finance Inc is a Delaware corporation. For information on translations, please e-mail [email protected]; for reprint, paperback, or audio rights, please e-mail [email protected]. Apress titles may be purchased in bulk for academic, corporate, or promotional use. eBook versions and licenses are also available for most titles. For more information, reference our Print and eBook Bulk Sales web page at www.apress.com/bulk-sales. Any source code or other supplementary material referenced by the author in this book is available to readers on GitHub via the book’s product page, located at www.apress.com/9781484258835. For more detailed information, please visit www.apress.com/source-code. Printed on acid-free paper

This is for Alexander and Henry, who are both going to learn programming soon. If their amazing expertise with Minecraft is anything to go by, they will be brilliant at it. —Ivor Horton For my wonderful family. For all your love and support. —Peter Van Weert

Table of Contents About the Authors�������������������������������������������������������������������������������������������������xxiii About the Technical Reviewer�������������������������������������������������������������������������������xxv Introduction���������������������������������������������������������������������������������������������������������xxvii

■Chapter ■ 1: Basic Ideas������������������������������������������������������������������������������������������� 1 Modern C++��������������������������������������������������������������������������������������������������������������������� 1 Standard Libraries������������������������������������������������������������������������������������������������������������ 2 C++ Program Concepts���������������������������������������������������������������������������������������������������� 3 Source Files�������������������������������������������������������������������������������������������������������������������������������������������� 3 Comments and Whitespace�������������������������������������������������������������������������������������������������������������������� 4 Standard Library Modules���������������������������������������������������������������������������������������������������������������������� 5 Functions������������������������������������������������������������������������������������������������������������������������������������������������ 5 Statements��������������������������������������������������������������������������������������������������������������������������������������������� 6 Data Input and Output���������������������������������������������������������������������������������������������������������������������������� 7 return Statements���������������������������������������������������������������������������������������������������������������������������������� 7 Namespaces������������������������������������������������������������������������������������������������������������������������������������������� 8 Names and Keywords����������������������������������������������������������������������������������������������������������������������������� 8

Classes and Objects��������������������������������������������������������������������������������������������������������� 9 Templates������������������������������������������������������������������������������������������������������������������������� 9 Code Appearance and Programming Style��������������������������������������������������������������������� 10 Creating an Executable�������������������������������������������������������������������������������������������������� 11 Procedural and Object-Oriented Programming�������������������������������������������������������������� 12

v

■ Table of Contents

Representing Numbers��������������������������������������������������������������������������������������������������� 13 Binary Numbers������������������������������������������������������������������������������������������������������������������������������������ 13 Hexadecimal Numbers������������������������������������������������������������������������������������������������������������������������� 15 Negative Binary Numbers��������������������������������������������������������������������������������������������������������������������� 16 Octal Values������������������������������������������������������������������������������������������������������������������������������������������ 18 Big-Endian and Little-Endian Systems������������������������������������������������������������������������������������������������� 18 Floating-Point Numbers������������������������������������������������������������������������������������������������������������������������ 19

Representing Characters������������������������������������������������������������������������������������������������ 21 ASCII Codes������������������������������������������������������������������������������������������������������������������������������������������ 21 UCS and Unicode���������������������������������������������������������������������������������������������������������������������������������� 22

C++ Source Characters�������������������������������������������������������������������������������������������������� 22 Escape Sequences������������������������������������������������������������������������������������������������������������������������������� 23

Summary������������������������������������������������������������������������������������������������������������������������ 25 ■Chapter ■ 2: Introducing Fundamental Types of Data�������������������������������������������� 27 Variables, Data, and Data Types������������������������������������������������������������������������������������� 27 Defining Integer Variables�������������������������������������������������������������������������������������������������������������������� 28 Zero Initialization���������������������������������������������������������������������������������������������������������������������������������� 32 Defining Variables with Fixed Values���������������������������������������������������������������������������������������������������� 32

Integer Literals��������������������������������������������������������������������������������������������������������������� 32 Decimal Integer Literals����������������������������������������������������������������������������������������������������������������������� 32 Hexadecimal Literals���������������������������������������������������������������������������������������������������������������������������� 34 Octal Literals����������������������������������������������������������������������������������������������������������������������������������������� 34 Binary Literals�������������������������������������������������������������������������������������������������������������������������������������� 34

Calculations with Integers���������������������������������������������������������������������������������������������� 35 Compound Arithmetic Expressions������������������������������������������������������������������������������������������������������� 36

Assignment Operations�������������������������������������������������������������������������������������������������� 37 The op= Assignment Operators������������������������������������������������������������������������������������������������������������ 40

The sizeof Operator�������������������������������������������������������������������������������������������������������� 41 Incrementing and Decrementing Integers���������������������������������������������������������������������� 42 Postfix Increment and Decrement Operations�������������������������������������������������������������������������������������� 42 vi

■ Table of Contents

Defining Floating-Point Variables����������������������������������������������������������������������������������� 44 Floating-Point Literals���������������������������������������������������������������������������������������������������� 45 Floating-Point Calculations�������������������������������������������������������������������������������������������� 45 Mathematical Constants����������������������������������������������������������������������������������������������������������������������� 46 Mathematical Functions����������������������������������������������������������������������������������������������������������������������� 47 Invalid Floating-Point Results��������������������������������������������������������������������������������������������������������������� 50 Pitfalls��������������������������������������������������������������������������������������������������������������������������������������������������� 51

Mixed Expressions and Type Conversion������������������������������������������������������������������������ 52 Explicit Type Conversion������������������������������������������������������������������������������������������������� 53 Old-Style Casts������������������������������������������������������������������������������������������������������������������������������������� 56

Formatting Strings��������������������������������������������������������������������������������������������������������� 56 Formatting Stream Output�������������������������������������������������������������������������������������������������������������������� 57 String Formatting with std::format()����������������������������������������������������������������������������������������������������� 57

Finding the Limits����������������������������������������������������������������������������������������������������������� 63 Finding Other Properties of Fundamental Types����������������������������������������������������������������������������������� 64

Working with Character Variables���������������������������������������������������������������������������������� 65 Working with Unicode Characters�������������������������������������������������������������������������������������������������������� 66

The auto Keyword���������������������������������������������������������������������������������������������������������� 67 Summary������������������������������������������������������������������������������������������������������������������������ 68 ■Chapter ■ 3: Working with Fundamental Data Types���������������������������������������������� 71 Operator Precedence and Associativity�������������������������������������������������������������������������� 71 Bitwise Operators����������������������������������������������������������������������������������������������������������� 73 The Bitwise Shift Operators������������������������������������������������������������������������������������������������������������������ 74 Logical Operations on Bit Patterns������������������������������������������������������������������������������������������������������� 77

The Lifetime of a Variable����������������������������������������������������������������������������������������������� 84 Global Variables�������������������������������������������������������������������������������������������������������������� 85 Enumerated Data Types�������������������������������������������������������������������������������������������������� 89 Aliases for Data Types���������������������������������������������������������������������������������������������������� 93 Summary������������������������������������������������������������������������������������������������������������������������ 94 vii

■ Table of Contents

■Chapter ■ 4: Making Decisions������������������������������������������������������������������������������� 97 Comparing Data Values�������������������������������������������������������������������������������������������������� 97 Applying the Comparison Operators����������������������������������������������������������������������������������������������������� 98 Comparing Floating-Point Values������������������������������������������������������������������������������������������������������� 100 The Spaceship Operator��������������������������������������������������������������������������������������������������������������������� 100

The if Statement����������������������������������������������������������������������������������������������������������� 103 Nested if Statements�������������������������������������������������������������������������������������������������������������������������� 107 Character Classification and Conversion�������������������������������������������������������������������������������������������� 108

The if-else Statement��������������������������������������������������������������������������������������������������� 110 Nested if-else Statements������������������������������������������������������������������������������������������������������������������ 112 Understanding Nested ifs������������������������������������������������������������������������������������������������������������������� 113

Logical Operators��������������������������������������������������������������������������������������������������������� 115 Logical AND���������������������������������������������������������������������������������������������������������������������������������������� 115 Logical OR������������������������������������������������������������������������������������������������������������������������������������������� 116 Logical Negation��������������������������������������������������������������������������������������������������������������������������������� 116 Combining Logical Operators������������������������������������������������������������������������������������������������������������� 117 Logical Operators on Integer Operands���������������������������������������������������������������������������������������������� 119 Logical Operators vs. Bitwise Operators�������������������������������������������������������������������������������������������� 119

The Conditional Operator���������������������������������������������������������������������������������������������� 121 The switch Statement�������������������������������������������������������������������������������������������������� 123 Fallthrough����������������������������������������������������������������������������������������������������������������������������������������� 128

Statement Blocks and Variable Scope�������������������������������������������������������������������������� 130 Initialization Statements��������������������������������������������������������������������������������������������������������������������� 131

Summary���������������������������������������������������������������������������������������������������������������������� 132 ■Chapter ■ 5: Arrays and Loops����������������������������������������������������������������������������� 135 Arrays��������������������������������������������������������������������������������������������������������������������������� 135 Using an Array������������������������������������������������������������������������������������������������������������������������������������ 135

Understanding Loops��������������������������������������������������������������������������������������������������� 137 The for Loop����������������������������������������������������������������������������������������������������������������� 138 Avoiding Magic Numbers��������������������������������������������������������������������������������������������� 141 viii

■ Table of Contents

Defining the Array Size with the Braced Initializer������������������������������������������������������� 142 Determining the Size of an Array��������������������������������������������������������������������������������� 143 Controlling a for Loop with Floating-Point Values�������������������������������������������������������� 145 More Complex for Loop Control Expressions���������������������������������������������������������������� 147 The Comma Operator�������������������������������������������������������������������������������������������������������������������������� 148

The Range-Based for Loop������������������������������������������������������������������������������������������� 149 The while Loop������������������������������������������������������������������������������������������������������������� 150 The do-while Loop�������������������������������������������������������������������������������������������������������� 153 Nested Loops���������������������������������������������������������������������������������������������������������������� 155 Skipping Loop Iterations����������������������������������������������������������������������������������������������� 158 Breaking Out of a Loop������������������������������������������������������������������������������������������������� 159 Indefinite Loops���������������������������������������������������������������������������������������������������������������������������������� 159

Controlling a for Loop with Unsigned Integers������������������������������������������������������������� 163 Arrays of Characters����������������������������������������������������������������������������������������������������� 165 Multidimensional Arrays����������������������������������������������������������������������������������������������� 168 Initializing Multidimensional Arrays���������������������������������������������������������������������������������������������������� 170 Multidimensional Character Arrays���������������������������������������������������������������������������������������������������� 173

Allocating an Array at Runtime������������������������������������������������������������������������������������� 174 Alternatives to Using an Array�������������������������������������������������������������������������������������� 177 Using array Containers������������������������������������������������������������������������������������������������������������� 177 Using std::vector Containers�������������������������������������������������������������������������������������������������������� 183

Summary���������������������������������������������������������������������������������������������������������������������� 188 ■Chapter ■ 6: Pointers and References������������������������������������������������������������������ 191 What Is a Pointer?�������������������������������������������������������������������������������������������������������� 191 The Address-Of Operator���������������������������������������������������������������������������������������������� 194 The Indirection Operator����������������������������������������������������������������������������������������������� 195 Why Use Pointers?������������������������������������������������������������������������������������������������������� 197 Pointers to Type char���������������������������������������������������������������������������������������������������� 197 Arrays of Pointers������������������������������������������������������������������������������������������������������������������������������� 200 ix

■ Table of Contents

Constant Pointers and Pointers to Constants��������������������������������������������������������������� 202 Pointers and Arrays������������������������������������������������������������������������������������������������������ 205 Pointer Arithmetic������������������������������������������������������������������������������������������������������������������������������� 205 Using Pointer Notation with an Array Name��������������������������������������������������������������������������������������� 208

Dynamic Memory Allocation����������������������������������������������������������������������������������������� 210 The Stack and the Free Store������������������������������������������������������������������������������������������������������������� 211 Using the new and delete Operators�������������������������������������������������������������������������������������������������� 212 Dynamic Allocation of Arrays�������������������������������������������������������������������������������������������������������������� 213

Member Selection Through a Pointer��������������������������������������������������������������������������� 217 Hazards of Dynamic Memory Allocation����������������������������������������������������������������������� 218 Dangling Pointers and Multiple Deallocations������������������������������������������������������������������������������������ 218 Allocation/Deallocation Mismatch������������������������������������������������������������������������������������������������������ 218 Memory Leaks������������������������������������������������������������������������������������������������������������������������������������ 219 Fragmentation of the Free Store��������������������������������������������������������������������������������������������������������� 219

Golden Rule of Dynamic Memory Allocation���������������������������������������������������������������� 220 Raw Pointers and Smart Pointers�������������������������������������������������������������������������������� 221 Using unique_ptr Pointers����������������������������������������������������������������������������������������������������������� 222 Using shared_ptr Pointers����������������������������������������������������������������������������������������������������������� 225

Understanding References������������������������������������������������������������������������������������������� 229 Defining References��������������������������������������������������������������������������������������������������������������������������� 229 Using a Reference Variable in a Range-Based for Loop��������������������������������������������������������������������� 231

Summary���������������������������������������������������������������������������������������������������������������������� 232 ■Chapter ■ 7: Working with Strings����������������������������������������������������������������������� 235 A Better Class of String������������������������������������������������������������������������������������������������ 235 Defining string Objects����������������������������������������������������������������������������������������������������������������������� 236 Operations with String Objects����������������������������������������������������������������������������������������������������������� 240 Accessing Characters in a String������������������������������������������������������������������������������������������������������� 244 Accessing Substrings������������������������������������������������������������������������������������������������������������������������� 246 Comparing Strings������������������������������������������������������������������������������������������������������������������������������ 247

x

■ Table of Contents

Searching Strings������������������������������������������������������������������������������������������������������������������������������� 255 Modifying a String������������������������������������������������������������������������������������������������������������������������������ 262 std::string vs. std::vector�������������������������������������������������������������������������������������������������������� 267

Converting Strings into Numbers��������������������������������������������������������������������������������� 268 Strings of International Characters������������������������������������������������������������������������������ 268 Strings of wchar_t Characters������������������������������������������������������������������������������������������������������������ 269 Objects That Contain Unicode Strings������������������������������������������������������������������������������������������������ 270

Raw String Literals������������������������������������������������������������������������������������������������������� 271 Summary���������������������������������������������������������������������������������������������������������������������� 272 ■Chapter ■ 8: Defining Functions��������������������������������������������������������������������������� 275 Segmenting Your Programs������������������������������������������������������������������������������������������ 275 Functions in Classes��������������������������������������������������������������������������������������������������������������������������� 276 Characteristics of a Function�������������������������������������������������������������������������������������������������������������� 276

Defining Functions������������������������������������������������������������������������������������������������������� 276 The Function Body������������������������������������������������������������������������������������������������������������������������������ 279 Return Values������������������������������������������������������������������������������������������������������������������������������������� 280 Function Declarations������������������������������������������������������������������������������������������������������������������������� 281

Passing Arguments to a Function��������������������������������������������������������������������������������� 282 Pass-by-Value������������������������������������������������������������������������������������������������������������������������������������� 283 Pass-by-Reference����������������������������������������������������������������������������������������������������������������������������� 291

Default Argument Values���������������������������������������������������������������������������������������������� 298 Multiple Default Parameter Values����������������������������������������������������������������������������������������������������� 299

Arguments to main()����������������������������������������������������������������������������������������������������� 301 Returning Values from a Function�������������������������������������������������������������������������������� 302 Returning a Pointer����������������������������������������������������������������������������������������������������������������������������� 302 Returning a Reference������������������������������������������������������������������������������������������������������������������������ 307 Returning vs. Output Parameters������������������������������������������������������������������������������������������������������� 307 Return Type Deduction������������������������������������������������������������������������������������������������������������������������ 308

Static Variables������������������������������������������������������������������������������������������������������������� 310

xi

■ Table of Contents

Function Overloading��������������������������������������������������������������������������������������������������� 311 Overloading and Pointer Parameters�������������������������������������������������������������������������������������������������� 313 Overloading and Reference Parameters��������������������������������������������������������������������������������������������� 313 Overloading and const Parameters���������������������������������������������������������������������������������������������������� 315 Overloading and Default Argument Values����������������������������������������������������������������������������������������� 317

Recursion��������������������������������������������������������������������������������������������������������������������� 318 Basic Examples of Recursion������������������������������������������������������������������������������������������������������������� 319 Recursive Algorithms�������������������������������������������������������������������������������������������������������������������������� 320

Summary���������������������������������������������������������������������������������������������������������������������� 327 ■Chapter ■ 9: Vocabulary Types����������������������������������������������������������������������������� 333 Working with Optional Values��������������������������������������������������������������������������������������� 333 std::optional���������������������������������������������������������������������������������������������������������������������������������������� 335

String Views: The New Reference-to-const-string������������������������������������������������������� 337 Using String View Function Parameters��������������������������������������������������������������������������������������������� 339 A Proper Motivation���������������������������������������������������������������������������������������������������������������������������� 340

Spans: The New Reference-to-vector or -array����������������������������������������������������������� 340 Spans vs. Views���������������������������������������������������������������������������������������������������������������������������������� 342 Spans of const Elements�������������������������������������������������������������������������������������������������������������������� 342 Fixed-Size Spans�������������������������������������������������������������������������������������������������������������������������������� 343

Summary���������������������������������������������������������������������������������������������������������������������� 344 ■Chapter ■ 10: Function Templates������������������������������������������������������������������������ 347 Function Templates������������������������������������������������������������������������������������������������������ 347 Creating Instances of a Function Template������������������������������������������������������������������ 348 Template Type Parameters������������������������������������������������������������������������������������������� 350 Explicit Template Arguments���������������������������������������������������������������������������������������� 351 Function Template Specialization��������������������������������������������������������������������������������� 351 Function Templates and Overloading��������������������������������������������������������������������������� 352 Function Templates with Multiple Parameters������������������������������������������������������������� 354 Return Type Deduction in Templates���������������������������������������������������������������������������� 355 decltype(auto)������������������������������������������������������������������������������������������������������������������������������������� 356 xii

■ Table of Contents

Default Values for Template Parameters���������������������������������������������������������������������� 357 Non-Type Template Parameters����������������������������������������������������������������������������������� 358 Templates for Functions with Fixed-Size Array Arguments���������������������������������������������������������������� 360

Abbreviated Function Templates���������������������������������������������������������������������������������� 362 Limitations to Abbreviated Function Templates���������������������������������������������������������������������������������� 362

Summary���������������������������������������������������������������������������������������������������������������������� 363 ■Chapter ■ 11: Modules and Namespaces������������������������������������������������������������� 365 Modules������������������������������������������������������������������������������������������������������������������������ 365 Your First Module�������������������������������������������������������������������������������������������������������������������������������� 367 Export Blocks�������������������������������������������������������������������������������������������������������������������������������������� 369 Separating Interface from Implementation���������������������������������������������������������������������������������������� 370 Reachability vs. Visibility�������������������������������������������������������������������������������������������������������������������� 375 Exporting Import Declarations������������������������������������������������������������������������������������������������������������ 376 Managing Larger Modules������������������������������������������������������������������������������������������������������������������ 377 Global Module Fragments������������������������������������������������������������������������������������������������������������������� 381

Namespaces����������������������������������������������������������������������������������������������������������������� 382 The Global Namespace����������������������������������������������������������������������������������������������������������������������� 382 Defining a Namespace����������������������������������������������������������������������������������������������������������������������� 383 Nested Namespaces��������������������������������������������������������������������������������������������������������������������������� 385 Namespaces and Modules����������������������������������������������������������������������������������������������������������������� 386 Functions and Namespaces��������������������������������������������������������������������������������������������������������������� 387 Using Directives and Declarations������������������������������������������������������������������������������������������������������ 389 Namespace Aliases���������������������������������������������������������������������������������������������������������������������������� 391

Summary���������������������������������������������������������������������������������������������������������������������� 391 ■Chapter ■ 12: Defining Your Own Data Types������������������������������������������������������� 395 Classes and Object-Oriented Programming����������������������������������������������������������������� 395 Encapsulation������������������������������������������������������������������������������������������������������������������������������������� 396 Inheritance������������������������������������������������������������������������������������������������������������������������������������������ 399 Polymorphism������������������������������������������������������������������������������������������������������������������������������������� 400

Terminology������������������������������������������������������������������������������������������������������������������ 402 xiii

■ Table of Contents

Defining a Class����������������������������������������������������������������������������������������������������������� 402 Creating Objects of a Class����������������������������������������������������������������������������������������������������������������� 404

Constructors����������������������������������������������������������������������������������������������������������������� 405 Default Constructors��������������������������������������������������������������������������������������������������������������������������� 405 Defining a Class Constructor�������������������������������������������������������������������������������������������������������������� 406 Using the default Keyword����������������������������������������������������������������������������������������������������������������� 408 Defining Functions Outside the Class������������������������������������������������������������������������������������������������� 408 Default Arguments for Constructor Parameters��������������������������������������������������������������������������������� 409 Using a Member Initializer List����������������������������������������������������������������������������������������������������������� 410 Using the explicit Keyword����������������������������������������������������������������������������������������������������������������� 411 Delegating Constructors��������������������������������������������������������������������������������������������������������������������� 413 The Copy Constructor������������������������������������������������������������������������������������������������������������������������� 415

Defining Classes in Modules���������������������������������������������������������������������������������������� 417 Accessing Private Class Members������������������������������������������������������������������������������� 418 The this Pointer������������������������������������������������������������������������������������������������������������ 420 Returning this from a Function����������������������������������������������������������������������������������������������������������� 421

const Objects and const Member Functions���������������������������������������������������������������� 422 const Member Functions�������������������������������������������������������������������������������������������������������������������� 423 const Correctness������������������������������������������������������������������������������������������������������������������������������� 425 Overloading on const�������������������������������������������������������������������������������������������������������������������������� 426 Casting Away const���������������������������������������������������������������������������������������������������������������������������� 427 Using the mutable Keyword���������������������������������������������������������������������������������������������������������������� 428

Friends������������������������������������������������������������������������������������������������������������������������� 429 The Friend Functions of a Class��������������������������������������������������������������������������������������������������������� 429 Friend Classes������������������������������������������������������������������������������������������������������������������������������������ 431

Arrays of Class Objects������������������������������������������������������������������������������������������������ 432 The Size of a Class Object�������������������������������������������������������������������������������������������� 434 Static Members of a Class������������������������������������������������������������������������������������������� 434 Static Member Variables��������������������������������������������������������������������������������������������������������������������� 435 Accessing Static Member Variables��������������������������������������������������������������������������������������������������� 438 Static Constants��������������������������������������������������������������������������������������������������������������������������������� 438 xiv

■ Table of Contents

Static Member Variables of the Class Type Itself�������������������������������������������������������������������������������� 439 Static Member Functions������������������������������������������������������������������������������������������������������������������� 440

Destructors������������������������������������������������������������������������������������������������������������������� 441 Using Pointers as Class Members�������������������������������������������������������������������������������� 444 The Truckload Example����������������������������������������������������������������������������������������������������������������������� 444

Nested Classes������������������������������������������������������������������������������������������������������������� 459 Nested Classes with Public Access���������������������������������������������������������������������������������������������������� 460

Summary���������������������������������������������������������������������������������������������������������������������� 464 ■Chapter ■ 13: Operator Overloading��������������������������������������������������������������������� 467 Implementing Operators for a Class����������������������������������������������������������������������������� 467 Operator Overloading�������������������������������������������������������������������������������������������������������������������������� 468 Implementing an Overloaded Operator����������������������������������������������������������������������������������������������� 468 Nonmember Operator Functions�������������������������������������������������������������������������������������������������������� 471 Implementing Full Support for an Operator���������������������������������������������������������������������������������������� 471

Operators That Can Be Overloaded������������������������������������������������������������������������������ 473 Restrictions and Key Guideline����������������������������������������������������������������������������������������������������������� 474

Operator Function Idioms��������������������������������������������������������������������������������������������� 476 Supporting All Comparison Operators�������������������������������������������������������������������������� 477 Defaulting Comparison Operators������������������������������������������������������������������������������������������������������ 481

Overloading the % : ; . ? * + - / ^ & | ~ ! = , \ ” ’

Chapter 1 ■ Basic Ideas

This is easy and straightforward. You have 96 characters that you can use, and it’s likely that these will accommodate your needs most of the time. Most of the time the basic source character set will be adequate, but occasionally you’ll need characters that aren’t in it. You can, at least in theory, include Unicode characters in a C++ name as well. Which Unicode encoding to use, and which subset of Unicode is supported, depends on your compiler. Both character and string data can similarly include Unicode characters as well. If you want to include a Unicode character in a character or string literal (literals are discussed in Chapter 2), though, in a form that is accepted by any compiler, you should use the hexadecimal representation of its code point. That is: you should then enter it either as \udddd or as \Udddddddd, where d is a hexadecimal digit. Note the lowercase u in the first case and the uppercase U in the second; either is acceptable.

Escape Sequences When you want to use character constants such as a single character or a character string in a program, certain characters are problematic. Obviously, you can’t enter characters such as newline directly as character constants, as they’ll just do what they’re supposed to do: go to a new line in your source code file (the only exception to this rule are raw string literals, which are covered in Chapter 7). You can enter these problem characters in character constants by means of an escape sequence. An escape sequence is an indirect way of specifying a character, and it always begins with a backslash. Table 1-3 shows the most commonly used escape sequences. Table 1-3.  The Most Commonly Used Escape Sequences

Escape Sequence

Character

\n

Newline

\r

Carriage return (part of the \r\n newline sequence for Windows)

\t

Horizontal tab

\\

Backslash (\)

\"

Double quote (") in string literals

\'

Single quote (') in character literals

The first three escape sequences in Table 1-3 represent various line break or whitespace characters. The last three characters, however, are more regular characters, yet at times problematic to represent directly in code. Clearly, the backslash character itself is difficult because it signals the start of an escape sequence, and the single and double quote characters because they are used as delimiters for literals, as in the constant 'A' or the string "text". This program that uses escape sequences outputs a message to the screen. To see it, you’ll need to enter, compile, link, and execute the code: // Ex1_02.cpp // Using escape sequences import ; int main() {   std::cout > 4) & ~(~0u -8         i second;   std::cout 8} {:>8} {:>20}\n";   // Output column headings   std::cout 8}", '|');   for (auto w : weight_lbs)     std::cout "  push_back(66);           // Add an element containing 66 The -> operator is formed by a minus sign and a greater-than character and is referred to as the arrow operator or indirect member selection operator. The arrow is much more expressive of what is happening here. You’ll be using this operator extensively later in the book.

Hazards of Dynamic Memory Allocation There are many kinds of serious problems you may run into when you allocate memory dynamically using new. In this section, we name the most common ones. Unfortunately, these hazards are all too real, as any developer who has worked with new and delete will corroborate. As a C++ developer, a significant portion of the more serious bugs you deal with often boil down to mismanagement of dynamic memory. In this section, we will thus paint you a seemingly very bleak picture, filled with all kinds of hazards of dynamic memory. But don’t despair. We will show you the way out of this treacherous minefield soon enough! Right after this section we will list the proven idioms and utilities that actually make it easy for you to avoid most if not all of these problems. In fact, you already know about one such utility: the std::vector container. This container is almost always the better choice over allocating dynamic memory directly using new[]. Other facilities of the Standard Library to better manage dynamic memory are discussed in upcoming sections. But first let’s dwell on all these lovely risks, hazards, pitfalls, and other perils alike that are associated with dynamic memory.

Dangling Pointers and Multiple Deallocations A dangling pointer, as you know, is a pointer variable that still contains the address to free store memory that has already been deallocated by either delete or delete[]. Dereferencing a dangling pointer makes you read from or, often worse, write to memory that might already be allocated to and used by other parts of your program, resulting in all kinds of unpredictable and unexpected results. Multiple deallocations, which occur when you deallocate an already deallocated (and hence dangling) pointer for a second time using either delete or delete[], is another recipe for disaster. We taught you one basic strategy already to guard yourself against dangling pointers, that is, to always reset a pointer to nullptr after the memory it points to is released. In more complex programs, however, different parts of the code often collaborate by accessing the same memory—an object or an array of objects—all through distinct copies of the same pointer. In such cases our simple strategy rapidly falls short. Which part of the code is going to call delete/delete[]? And when? That is, how do you be sure that no other part of the code is still using the same dynamically allocated memory?

Allocation/Deallocation Mismatch A dynamically allocated array, allocated using new[], is captured in a regular pointer variable. But so is a single allocated value that is allocated using new: int* single_int{ new int{123} };     // Pointer to a single integer, initialized with 123 int* array_of_ints{ new int[123] };  // Pointer to an array of 123 uninitialized integers

218

Chapter 6 ■ Pointers and References

After this, the compiler has no way to distinguish between the two, especially once such a pointer gets passed around different parts of the program. This means that the following two statements will compile without error (and in many cases even without a warning): delete[] single_int;       // Wrong! delete array_of_ints;      // Wrong! What’ll happen if you mismatch your allocation and deallocation operators depends entirely on the implementation associated with your compiler. But it won’t be anything good.

■■Caution Every new must be paired with a single delete; every new[] must be paired with a single delete[]. Any other sequence of events leads to either undefined behavior or memory leaks (discussed next).

Memory Leaks A memory leak occurs when you allocate memory using new or new[] and fail to release it. If you lose the address of free store memory you have allocated, by overwriting the address in the pointer you were using to access it, for instance, you have a memory leak. This often occurs in a loop, and it’s easier to create this kind of problem than you might think. The effect is that your program gradually consumes more and more of the free store, with the program potentially slowing more and more down, or even failing at the point when all of the free store has been allocated. When it comes to scope, pointers are just like any other variable. The lifetime of a pointer extends from the point at which you define it in a block to the closing brace of the block. After that it no longer exists, so the address it contained is no longer accessible. If a pointer containing the address of a block of memory in the free store goes out of scope, then it’s no longer possible to delete the memory. It’s still relatively easy to see where you’ve simply forgotten to use delete to free memory when use of the memory ceases at a point close to where you allocated it, but you’d be surprised how often programmers make mistakes like this, especially if, for instance, return statements creep in between the allocation and deallocation of your variable. And, naturally, memory leaks are even more difficult to spot in complex programs, where memory may be allocated in one part of a program and should be released in a completely separate part. One basic strategy for avoiding memory leaks is to immediately add the delete operation at an appropriate place each time you use the new operator. But this strategy is by no means fail-safe. We cannot stress this enough: humans, even C++ programmers, are fallible creatures. So, whenever you manipulate dynamic memory directly, you will, sooner or later, introduce memory leaks. Even if it works at the time of writing, all too often bugs find their way into the program as it evolves further. return statements are added, conditional tests change, exceptions are thrown (see Chapter 16), and so on. And all of a sudden there are scenarios where your memory is no longer freed correctly!

Fragmentation of the Free Store Memory fragmentation can arise in programs that frequently allocate and release memory blocks. Each time the new operator is used, it allocates a contiguous block of bytes. If you create and destroy many memory blocks of different sizes, it’s possible to arrive at a situation in which the allocated memory is interspersed with small blocks of free memory, none of which is large enough to accommodate a new memory allocation

219

Chapter 6 ■ Pointers and References

request by your program. The aggregate of the free memory can be quite large, but if all the individual blocks are small (smaller than a current allocation request), the allocation request will fail. Figure 6-6 illustrates the effect of memory fragmentation.

Memory blocks that have been released but are smaller than the next block requested to be allocated. The total amount of free memory may be large, but it's all in small blocks.

A new block of memory is requested that is much smaller than the total memory that is free, but it cannot be allocated because it's larger than any available block.

Rest of the Free Store Fragmented in Same way.

Memory Allocated and Still in Use Figure 6-6.  Fragmentation of the free store We mention this problem here mostly for completeness because it arises relatively infrequently these days. Virtual memory provides a large memory address space even on quite modest computers. And the algorithms behind new/delete are clever and designed to counteract such phenomena as much as possible. It’s only in rare cases that you need to worry about fragmentation anymore. For instance, for the most performance-critical parts of your code, operating on fragmented memory may seriously degrade performance. The way to avoid fragmentation of the free store then is not to allocate many small blocks of memory. Allocate larger blocks and manage the use of the memory yourself. But this is an advanced topic, well outside the scope of this book.

Golden Rule of Dynamic Memory Allocation After spending more than half a dozen pages learning how to use the new and delete operators, our golden rule of dynamic memory allocation may come as a bit of a surprise. It’s nevertheless one of the single most valuable pieces of advice we give you in this book:

■■Tip Never use the operators new, new[], delete, and delete[] directly in day-to-day coding. These operators have no place in modern C++ code. Always use either the std::vector container (to replace dynamic arrays) or a smart pointer (to dynamically allocate individual objects and manage their lifetimes). These high-level alternatives are much, much safer than the low-level memory management primitives and will help you tremendously by eradicating dangling pointers, multiple deallocations, allocation/deallocation mismatches, and memory leaks from your programs. 220

Chapter 6 ■ Pointers and References

The std::vector container you already know from the previous chapter, and smart pointers are explained in the next section. The main reason we still teach you about the low-level dynamic memory allocation primitives is not because we’d invite you to use them (often or at all), but because you will surely still encounter them in existing code. This also, unfortunately, implies that you will be tasked with fixing bugs caused by their use. (Bonus tip: a good first step would be to rewrite this code using better, more modern memory management utilities; more often than not, the underlying problem then reveals itself.) In your own code, you should normally avoid manipulating dynamic memory directly.

Raw Pointers and Smart Pointers All the pointer types we have discussed up to now are part of the C++ language. These are referred to as raw pointers because variables of these types contain nothing more than an address. A raw pointer can store the address of an automatic variable or a variable allocated in the free store. A smart pointer is an object that mimics a raw pointer in that it contains an address, and you can use it in the same way in many respects. Smart pointers are normally used only to store the address of memory allocated in the free store. A smart pointer does much more than a raw pointer, though. By far the most notable feature of a smart pointer is that you don’t have to worry about using the delete or delete[] operator to free the memory. It will be released automatically when it is no longer needed. This means that multiple deallocations, allocation/deallocation mismatches, and memory leaks will no longer be possible. If you consistently use smart pointers, dangling pointers will be a thing of the past as well. Smart pointers are particularly useful for managing class objects that you create dynamically, so smart pointers will be of greater relevance from Chapter 12 on. You can also store them in an array or vector container, which is again very useful when you are working with objects of a class type. Smart pointer types are defined by templates inside the module of the Standard Library, so you must import this module in your source file to use them. There are three types of smart pointers, all defined in the std namespace: •

A unique_ptr object behaves as a pointer to type T and is “unique” in the sense that there can be only one single unique_ptr object containing the same address. In other words, there can never be two or more unique_ptr objects pointing to the same memory address at the same time. A unique_ptr object is said to own what it points to exclusively. This uniqueness is enforced by the fact that the compiler will never allow you to copy a unique_ptr.2



A shared_ptr object also behaves as a pointer to type T, but in contrast with unique_ ptr there can be any number of shared_ptr objects that contain—or, share—the same address. Thus, shared_ptr objects allow shared ownership of an object in the free store. At any given moment, the number of shared_ptr objects that contain a given address in time is known by the runtime. This is called reference counting. The reference count for a shared_ptr containing a given free store address is incremented each time a new shared_ptr object is created containing that address, and it’s decremented when a shared_ptr containing the address is destroyed or assigned to point to a different address. When there are no shared_ptr objects containing a given address, the reference count will have dropped to zero, and the memory for the object at that address will be released automatically. All shared_ptr objects that point to the same address have access to the count of how many there are. You’ll understand how this is possible when you learn about classes in Chapter 12.

 In Chapter 18, you’ll learn that while copying a unique_ptr is not possible, you can “move” the address stored by one unique_ptr object to another by using the std::move() function. After this move operation the original smart pointer 2

will be empty again.

221

Chapter 6 ■ Pointers and References



A weak_ptr is linked to a shared_ptr and contains the same address. Creating a weak_ptr does not increment the reference count associated with the linked shared_ptr object, though, so a weak_ptr does not prevent the object pointed to from being destroyed. Its memory will still be released when the last shared_ptr referencing it is destroyed or reassigned to point to a different address, even when associated weak_ptr objects still exist. If this happens, the weak_ptr will nevertheless not contain a dangling pointer, at least not one that you could inadvertently access. The reason is that you cannot access the address encapsulated by a weak_ptr directly. Instead, the compiler will force you to first create a shared_ptr out of it that refers to the same address. If the memory address for the weak_ptr is still valid, forcing you to create a shared_ptr first ensures that the reference count is again incremented and that the pointer can be used safely again. If the memory is released already, however, this operation will result in a shared_ptr containing nullptr.

One use for having weak_ptr objects is to avoid so-called reference cycles with shared_ptr objects. Conceptually, a reference cycle is where a shared_ptr inside an object x points to some other object y that contains a shared_ptr, which points back to x. With this situation, neither x nor y can be destroyed. In practice, this may occur in ways that are more complicated. weak_ptr smart pointers allow you to break such reference cycles. Another use for weak pointers is the implementation of object caches. However, as you no doubt already started to sense, weak pointers are used only in more advanced use cases. As they are used only sporadically, we’ll not discuss them any further here. The other two smart pointer types, however, you should use literally all the time, so let’s dig a bit deeper into them.

Using unique_ptr Pointers A unique_ptr object stores an address uniquely, so the value to which it points is owned exclusively by the unique_ptr smart pointer. When the unique_ptr is destroyed, so is the value to which it points. Like all smart pointers, a unique_ptr is most useful when working with dynamically allocated objects. Objects then should not be shared by multiple parts of the program or where the lifetime of the dynamic object is naturally tied to a single other object in your program. One common use for a unique_ptr is to hold something called a polymorphic pointer, which in essence is a pointer to a dynamically allocated object that can be of any number of related class types. To cut a long story short, you’ll only fully appreciate this smart pointer type after learning all about class objects and polymorphism in Chapters 12 through 15. For now, our examples will simply use dynamically allocated values of fundamental types, which do not really excel in usefulness. What should become obvious already, though, is why these smart pointers are that much safer to use than the low-level allocation and deallocation primitives; that is, they make it impossible for you to forget or mismatch deallocation! In the (not so) old days, you had to create and initialize a unique_ptr object like this: std::unique_ptr pdata {new double{999.0}}; This creates pdata, which contains the address of a double variable in the free store that is initialized with 999.0. While this syntax remains valid, the recommended way to create a unique_ptr today is by means of the std::make_unique() function template (introduced by C++14). To define pdata, you should therefore normally use the following: std::unique_ptr pdata { std::make_unique(999.0) };

222

Chapter 6 ■ Pointers and References

The arguments to std::make_unique(...) are exactly those values that would otherwise appear in the braced initializer of a dynamic allocation of the form new T{...}. In our example, that’s the single double literal 999.0. To save you some typing, you’ll probably want to combine this syntax with the use of the auto keyword: auto pdata{ std::make_unique(999.0) }; That way, you only have to type the type of the dynamic variable, double, once.

■■Tip To create a std::unique_ptr object that points to a newly allocated T value, always use the std::make_unique () function. You can dereference pdata just like an ordinary pointer, and you can use the result in the same way: *pdata = 8888.0; std::cout         >=       15}", word);     if (!(++count % 5))       std::cout when accessing a member. The compiler takes care of that for you. Together with the this pointer, you can still capture other variables as well. You can combine this with a & or = capture default and/or any sequence of captures of named variables. Examples of such capture clauses are [=, this], [this, &counter] , and [x, this, &counter].

■■Caution The = capture default technically still implies that the this pointer is captured, but this behavior was deprecated in C++20 and should no longer be relied upon. That is, in older code, you may still encounter expressions such as this:    return *findOptimum(values, [=](double x, double y) {// Also captures this (deprecated)       return std::abs(x - m_number_to_search_for) < std::abs(y - m_number_to_search_for);     });

In C++20 code you should always capture the this pointer explicitly, never implicitly through = (your compiler will likely warn you if you do not).

The std::function Template The type of a function pointer is very different from that of a function object or lambda closure. The former is a pointer, and the latter is a class. At first sight, it might thus seem that the only way to write code that works for any conceivable callback—that is, a function pointer, function object, or lambda closure—is to use either auto or a template type parameter. This is what we did for our findOptimum() template in earlier examples. The same technique is heavily used throughout the Standard Library, as you will see in the next chapter.

735

Chapter 19 ■ First-Class Functions

Using templates does have its cost. You risk template code bloat, where the compiler has to generate specialized code for all different types of callbacks, even when it’s not required for performance reasons. It also has its limitations: What if you needed say a vector of callback functions—a vector that is potentially filled with a mixture of function pointers, function objects, and lambda closures? To cater to these types of scenarios, the module defines the std::function template. With objects of type std::function you can store, copy, move, and invoke any kind of function-like entity—be it a function pointer, function object, or lambda closure. The following example demonstrates precisely that: // Ex19_08.cpp // Using the std::function template import ; import ; #include        // for std::abs() // A global less() function bool less(int x, int y) { return x < y; } int main() {   int a{ 18 }, b{ 8 };   std::cout