- •Table of Contents
- •About the Author
- •About the Technical Reviewer
- •Acknowledgments
- •Software Entropy
- •Clean Code
- •C++11: The Beginning of a New Era
- •Who This Book Is For
- •Conventions Used in This Book
- •Sidebars
- •Notes, Tips, and Warnings
- •Code Samples
- •Coding Style
- •C++ Core Guidelines
- •Companion Website and Source Code Repository
- •UML Diagrams
- •The Need for Testing
- •Unit Tests
- •What About QA?
- •Rules for Good Unit Tests
- •Test Code Quality
- •Unit Test Naming
- •Unit Test Independence
- •One Assertion per Test
- •Independent Initialization of Unit Test Environments
- •Exclude Getters and Setters
- •Exclude Third-Party Code
- •Exclude External Systems
- •What Do We Do with the Database?
- •Don’t Mix Test Code with Production Code
- •Tests Must Run Fast
- •How Do You Find a Test’s Input Data?
- •Equivalence Partitioning
- •Boundary Value Analysis
- •Test Doubles (Fake Objects)
- •What Is a Principle?
- •KISS
- •YAGNI
- •It’s About Knowledge!
- •Building Abstractions Is Sometimes Hard
- •Information Hiding
- •Strong Cohesion
- •Loose Coupling
- •Be Careful with Optimizations
- •Principle of Least Astonishment (PLA)
- •The Boy Scout Rule
- •Collective Code Ownership
- •Good Names
- •Names Should Be Self-Explanatory
- •Use Names from the Domain
- •Choose Names at an Appropriate Level of Abstraction
- •Avoid Redundancy When Choosing a Name
- •Avoid Cryptic Abbreviations
- •Avoid Hungarian Notation and Prefixes
- •Avoid Using the Same Name for Different Purposes
- •Comments
- •Let the Code Tell the Story
- •Do Not Comment Obvious Things
- •Don’t Disable Code with Comments
- •Don’t Write Block Comments
- •Don’t Use Comments to Substitute Version Control
- •The Rare Cases Where Comments Are Useful
- •Documentation Generation from Source Code
- •Functions
- •One Thing, No More!
- •Let Them Be Small
- •“But the Call Time Overhead!”
- •Function Naming
- •Use Intention-Revealing Names
- •Parameters and Return Values
- •Avoid Flag Parameters
- •Avoid Output Parameters
- •Don’t Pass or Return 0 (NULL, nullptr)
- •Strategies for Avoiding Regular Pointers
- •Choose simple object construction on the stack instead of on the heap
- •In a function’s argument list, use (const) references instead of pointers
- •If it is inevitable to deal with a pointer to a resource, use a smart one
- •If an API returns a raw pointer...
- •The Power of const Correctness
- •About Old C-Style in C++ Projects
- •Choose C++ Strings and Streams over Old C-Style char*
- •Use C++ Casts Instead of Old C-Style Casts
- •Avoid Macros
- •Managing Resources
- •Resource Acquisition Is Initialization (RAII)
- •Smart Pointers
- •Unique Ownership with std::unique_ptr<T>
- •Shared Ownership with std::shared_ptr<T>
- •No Ownership, but Secure Access with std::weak_ptr<T>
- •Atomic Smart Pointers
- •Avoid Explicit New and Delete
- •Managing Proprietary Resources
- •We Like to Move It
- •What Are Move Semantics?
- •The Matter with Those lvalues and rvalues
- •rvalue References
- •Don’t Enforce Move Everywhere
- •The Rule of Zero
- •The Compiler Is Your Colleague
- •Automatic Type Deduction
- •Computations During Compile Time
- •Variable Templates
- •Don’t Allow Undefined Behavior
- •Type-Rich Programming
- •Know Your Libraries
- •Take Advantage of <algorithm>
- •Easier Parallelization of Algorithms Since C++17
- •Sorting and Output of a Container
- •More Convenience with Ranges
- •Non-Owning Ranges with Views
- •Comparing Two Sequences
- •Take Advantage of Boost
- •More Libraries That You Should Know About
- •Proper Exception and Error Handling
- •Prevention Is Better Than Aftercare
- •No Exception Safety
- •Basic Exception Safety
- •Strong Exception Safety
- •The No-Throw Guarantee
- •An Exception Is an Exception, Literally!
- •If You Can’t Recover, Get Out Quickly
- •Define User-Specific Exception Types
- •Throw by Value, Catch by const Reference
- •Pay Attention to the Correct Order of Catch Clauses
- •Interface Design
- •Attributes
- •noreturn (since C++11)
- •deprecated (since C++14)
- •nodiscard (since C++17)
- •maybe_unused (since C++17)
- •Concepts: Requirements for Template Arguments
- •The Basics of Modularization
- •Criteria for Finding Modules
- •Focus on the Domain of Your Software
- •Abstraction
- •Choose a Hierarchical Decomposition
- •Single Responsibility Principle (SRP)
- •Single Level of Abstraction (SLA)
- •The Whole Enchilada
- •Object-Orientation
- •Object-Oriented Thinking
- •Principles for Good Class Design
- •Keep Classes Small
- •Open-Closed Principle (OCP)
- •A Short Comparison of Type Erasure Techniques
- •Liskov Substitution Principle (LSP)
- •The Square-Rectangle Dilemma
- •Favor Composition over Inheritance
- •Interface Segregation Principle (ISP)
- •Acyclic Dependency Principle
- •Dependency Inversion Principle (DIP)
- •Don’t Talk to Strangers (The Law of Demeter)
- •Avoid Anemic Classes
- •Tell, Don’t Ask!
- •Avoid Static Class Members
- •Modules
- •The Drawbacks of #include
- •Three Options for Using Modules
- •Include Translation
- •Header Importation
- •Module Importation
- •Separating Interface and Implementation
- •The Impact of Modules
- •What Is Functional Programming?
- •What Is a Function?
- •Pure vs Impure Functions
- •Functional Programming in Modern C++
- •Functional Programming with C++ Templates
- •Function-Like Objects (Functors)
- •Generator
- •Unary Function
- •Predicate
- •Binary Functors
- •Binders and Function Wrappers
- •Lambda Expressions
- •Generic Lambda Expressions (C++14)
- •Lambda Templates (C++20)
- •Higher-Order Functions
- •Map, Filter, and Reduce
- •Filter
- •Reduce (Fold)
- •Fold Expressions in C++17
- •Pipelining with Range Adaptors (C++20)
- •Clean Code in Functional Programming
- •The Drawbacks of Plain Old Unit Testing (POUT)
- •Test-Driven Development as a Game Changer
- •The Workflow of TDD
- •TDD by Example: The Roman Numerals Code Kata
- •Preparations
- •The First Test
- •The Second Test
- •The Third Test and the Tidying Afterward
- •More Sophisticated Tests with a Custom Assertion
- •It’s Time to Clean Up Again
- •Approaching the Finish Line
- •Done!
- •The Advantages of TDD
- •When We Should Not Use TDD
- •TDD Is Not a Replacement for Code Reviews
- •Design Principles vs Design Patterns
- •Some Patterns and When to Use Them
- •Dependency Injection (DI)
- •The Singleton Anti-Pattern
- •Dependency Injection to the Rescue
- •Adapter
- •Strategy
- •Command
- •Command Processor
- •Composite
- •Observer
- •Factories
- •Simple Factory
- •Facade
- •The Money Class
- •Special Case Object (Null Object)
- •What Is an Idiom?
- •Some Useful C++ Idioms
- •The Power of Immutability
- •Substitution Failure Is Not an Error (SFINAE)
- •The Copy-and-Swap Idiom
- •Pointer to Implementation (PIMPL)
- •Structural Modeling
- •Component
- •Interface
- •Association
- •Generalization
- •Dependency
- •Template and Template Binding
- •Behavioral Modeling
- •Activity Diagram
- •Action
- •Control Flow Edge
- •Other Activity Nodes
- •Sequence Diagram
- •Lifeline
- •Message
- •State Diagram
- •State
- •Transitions
- •External Transitions
- •Internal Transitions
- •Trigger
- •Stereotypes
- •Bibliography
- •Index
Clean C++20
Sustainable Software Development
Patterns and Best Practices
—
Second Edition
—
Stephan Roth
Clean C++20
Sustainable Software Development
Patterns and Best Practices
Second Edition
Stephan Roth
Clean C++20: Sustainable Software Development Patterns and Best Practices
Stephan Roth
Bad Schwartau, Schleswig-Holstein, Germany
ISBN-13 (pbk): 978-1-4842-5948-1\ |
\ |
ISBN-13 (electronic): 978-1-4842-5949-8 |
https://doi.org/10.1007/978-1-4842-5949-8 |
|
|
Copyright © 2021 by Stephan Roth
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 by Jay Mantri 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 orders-ny@springer-sbm.com, 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 booktranslations@springernature.com; for reprint, paperback, or audio rights, please e-mail bookpermissions@springernature.com.
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 http://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/9781484259481. For more detailed information, please visit http://www.apress.com/source-code.
Printed on acid-free paper
To Caroline and Maximilian: my beloved and marvelous family.
Table of Contents
About the Author\ xiii About the Technical Reviewer\ xv Acknowledgments\ xvii
Chapter 1: Introduction 1
Software Entropy\ 3
Why C++?\ 4
Clean Code\ 6
C++11: The Beginning of a New Era 6
Who This Book Is For 7
Conventions Used in This Book\ 8
Sidebars\ 9
Notes, Tips, and Warnings\ 9
Code Samples 9
Companion Website and Source Code Repository\ 11
UML Diagrams 12
Chapter 2: Build a Safety Net 13
The Need for Testing\ 13 Introduction to Testing\ 16 Unit Tests\ 19 What About QA?\ 21 Rules for Good Unit Tests\ 22
Test Code Quality\ 22 Unit Test Naming\ 22
v
Table of Contents
Unit Test Independence\ 24 One Assertion per Test 25 Independent Initialization of Unit Test Environments 26 Exclude Getters and Setters\ 27 Exclude Third-Party Code\ 27 Exclude External Systems\ 28 What Do We Do with the Database?\ 28 Don’t Mix Test Code with Production Code 29 Tests Must Run Fast\ 32 How Do You Find a Test’s Input Data?\ 33 Test Doubles (Fake Objects)\ 36
Chapter 3: Be Principled 41
What Is a Principle?\ 41 KISS\ 42 YAGNI\ 43 DRY\ 44 It’s About Knowledge! 44 Building Abstractions Is Sometimes Hard\ 45 Information Hiding\ 48 Strong Cohesion 53 Loose Coupling 56 Be Careful with Optimizations 60 Principle of Least Astonishment (PLA)\ 61 The Boy Scout Rule\ 62 Collective Code Ownership 63
Chapter 4: Basics of Clean C++\ 65
Good Names 66 Names Should Be Self-Explanatory 68 Use Names from the Domain 70 Choose Names at an Appropriate Level of Abstraction 72
vi
Table of Contents
Avoid Redundancy When Choosing a Name\ 73 Avoid Cryptic Abbreviations 74 Avoid Hungarian Notation and Prefixes\ 75 Avoid Using the Same Name for Different Purposes\ 76 Comments 77 Let the Code Tell the Story 77 Do Not Comment Obvious Things\ 78 Don’t Disable Code with Comments\ 79 Don’t Write Block Comments 80 The Rare Cases Where Comments Are Useful\ 84 Functions\ 89 One Thing, No More!\ 93 Let Them Be Small\ 94 Function Naming\ 96 Use Intention-Revealing Names\ 97 Parameters and Return Values\ 98 About Old C-Style in C++ Projects 114 Choose C++ Strings and Streams over Old C-Style char* 115 Avoid Using printf( ), sprintf( ), gets( ), etc.\ 117 Choose Standard Library Containers over Simple C-Style Arrays\ 122 Use C++ Casts Instead of Old C-Style Casts\ 125 Avoid Macros\ 128
Chapter 5: Advanced Concepts of Modern C++ 131
Managing Resources\ 132 Resource Acquisition Is Initialization (RAII)\ 134 Smart Pointers 135 Avoid Explicit New and Delete 144 Managing Proprietary Resources\ 144
We Like to Move It 146 What Are Move Semantics? 146 The Matter with Those lvalues and rvalues\ 148
vii
Table of Contents
rvalue References\ 150 Don’t Enforce Move Everywhere\ 152 The Rule of Zero\ 153 The Compiler Is Your Colleague\ 159 Automatic Type Deduction\ 159 Computations During Compile Time\ 164 Variable Templates\ 167 Don’t Allow Undefined Behavior 169 Type-Rich Programming\ 171 Know Your Libraries\ 182 Take Advantage of <algorithm>\ 183 Take Advantage of Boost\ 194 More Libraries That You Should Know About 194 Proper Exception and Error Handling 196 Prevention Is Better Than Aftercare 197 An Exception Is an Exception, Literally! 202 If You Can’t Recover, Get Out Quickly 204 Define User-Specific Exception Types\ 205 Throw by Value, Catch by const Reference 207 Pay Attention to the Correct Order of Catch Clauses\ 208
Interface Design 209 Attributes 210 Concepts: Requirements for Template Arguments\ 215
Chapter 6: Modularization\ 221
The Basics of Modularization 222 Criteria for Finding Modules\ 222 The Whole Enchilada\ 227 Object-Orientation 227 Object-Oriented Thinking 228 Principles for Good Class Design 230
viii
Table of Contents
Modules\ 281 The Drawbacks of #include\ 281 Modules to the Rescue\ 283 Under the Hood 284 Three Options for Using Modules\ 286 The Impact of Modules\ 290
Chapter 7: Functional Programming\ 293
What Is Functional Programming? 295 What Is a Function?\ 296 Pure vs Impure Functions 297 Functional Programming in Modern C++\ 299 Functional Programming with C++ Templates 299 Function-Like Objects (Functors)\ 302 Binders and Function Wrappers\ 312 Lambda Expressions\ 315 Generic Lambda Expressions (C++14)\ 318 Lambda Templates (C++20)\ 319 Higher-Order Functions 322 Map, Filter, and Reduce\ 324 Pipelining with Range Adaptors (C++20) 329 Clean Code in Functional Programming 333
Chapter 8: Test-Driven Development\ 335
The Drawbacks of Plain Old Unit Testing (POUT) 336 Test-Driven Development as a Game Changer 338 The Workflow of TDD\ 339 TDD by Example: The Roman Numerals Code Kata 342 Preparations\ 343 The First Test\ 346 The Second Test\ 349 The Third Test and the Tidying Afterward\ 349
ix
Table of Contents
More Sophisticated Tests with a Custom Assertion 354 It’s Time to Clean Up Again\ 359 Approaching the Finish Line\ 362 Done!\ 364 The Advantages of TDD\ 367 When We Should Not Use TDD\ 369 TDD Is Not a Replacement for Code Reviews\ 371
Chapter 9: Design Patterns and Idioms\ 375
Design Principles vs Design Patterns\ 376
Some Patterns and When to Use Them 377
Dependency Injection (DI) 378
Adapter\ 394
Strategy\ 396
Command\ 402
Command Processor\ 407
Composite 412
Observer\ 416
Factories 422
Facade 425
The Money Class\ 427
Special Case Object (Null Object)\ 431
What Is an Idiom?\ 435
Some Useful C++ Idioms\ 436
Appendix A: Small UML Guide\ 451
Structural Modeling\ 452
Component\ 452
Class and Object 453
Interface\ 457
Association\ 459
x
Table of Contents
Generalization 462
Dependency 463
Template and Template Binding\ 465
Behavioral Modeling\ 466
Activity Diagram\ 466
Sequence Diagram\ 469
State Diagram 471
Stereotypes\ 474
Bibliography\ 477
Index 481
xi