![](/user_photo/_userpic.png)
- •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
Chapter 5 Advanced Concepts of Modern C++
Take Advantage of Boost
I can’t give a broad introduction into the famous Boost library (www.boost.org, distributed under the Boost Software License, Version 1.0) here. The library (in fact, it is a library of libraries) is too big and too powerful, and discussing it in detail is beyond the scope of this book. Furthermore, there are numerous good books and tutorials about Boost.
But I think that it is very important to know about this library and its content. Many problems and challenges that C++ developers face in their daily work can be pretty well solved with libraries from Boost.
Beyond that, Boost is a kind of “incubator” for several libraries that are sometimes accepted to become part of the C++ language standard, if they have a certain level of maturity. Be careful, that does not necessarily mean that they are fully compatible! For instance, std::thread (part of the standard since C++11) is partially equal to Boost. Thread, but there are some differences. For example, the Boost implementation supports thread cancellation, something that is available in the Standard Library only since C++20 (std::jthread). On the other hand, C++11 supports std::async, but Boost does not.
From my perspective, it is worth it to know the libraries from Boost, and to remember when you have a suitable problem that can be properly solved by them.
More Libraries That You Should Know About
Apart from Standard Library containers, <algorithm>, Ranges, and Boost, there are some more libraries out there that you might take into consideration when writing your code. Here is an incomplete list of libraries that are worth looking at when you are confronted with a certain suitable problem:
•\ Atomic types (<atomic>): A collection of templates and types available since C++11 that different threads can simultaneously operate on without raising undefined behavior (data races; see the sidebar about “Atomic Operations” in the section about smart
pointers). The central element is the class template std::atomic<T>, which can be used to define atomic types. For all integral data types, corresponding aliases are predefined, for example atomic_int32_t for std::atomic<int32_t>.
194
![](/html/75672/2303/html_cw6SR1shwt.1WUZ/htmlconvd-KksEvx207x1.jpg)
Chapter 5 Advanced Concepts of Modern C++
•\ Date and time utilities (<chrono>): Since C++11, the language provides a collection of types to represent clocks, time points, and durations. And with the latest standard C++20, dates and time zones have also been added. For instance, you can represent time intervals with the help of std::chrono::duration. And with std::chrono::system_clock, a system-wide real-time clock is
available. You can use the library since C++11 by just including the
<chrono> header.
•\ Pseudo-random number generator library (<random>): A library available since C++11 that provides classes for generating random and pseudo-random numbers. This library is a much better, more modern, and more powerful alternative to the old C library function combination srand() with rand(). With the <random> header, developers get a selection of Random Number Generators (RNG) with different engines (Minimum standard, 32-bit and 64- bit Mersenne Twister, etc.) and distributions (Normal, Uniform, Bernoulli, etc.) at hand.
•\ Regular expressions library (<regex>): Since C++11, a regular expressions library is available that can be used to perform pattern matching within strings. Also the replacement of text within a string based on regular expressions is supported. You can use the library since C++11 by just including the <regex> header.
•\ Filesystem library (<filesystem>): Since C++17, the Filesystem library has become part of the standard. Before it became part of the mainline C++ standard, it was a technical specification (ISO/ IEC TS 18822:2015). The operational system independent library provides various facilities for performing operations on file systems
and their components. With the help of <filesystem> you can create directories, copy files, iterate over directory entries, retrieve the size of a file, etc. You can use the library since C++17 by just including the
<filesystem> header.
Tip If you are currently not working according to the C++17 standard or higher, Boost.Filesystem could be an alternative.
195