
- •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 4 Basics of Clean C++
Can you see how much clarity a small change like this could bring to your code? We introduced a small member function that explains its intention by its semantic name. And at the place where the string operation originally could be found, we’ve replaced the direct invocation of std::string::substr() by a call of the new function.
Note The name of a function should express its intention/purpose, and not explain how it works.
How the job is done—that’s what you should learn from the code in the function’s body. Don’t explain the how in a function’s name. Instead, express the purpose of the function from a business perspective.
In addition, we have another advantage. The partial functionality of how the header is extracted from the HTML page has been quasi-isolated and is now more easily replaceable without fumbling around at those places where the function is called.
Parameters and Return Values
After we discussed function names in detail, there is another aspect that is important for good and clean functions: the function’s parameters and return values. These both also contribute significantly to the fact that a function or method can be well understood and is easily usable by clients.
Number ofParameters
How many parameters should a function or method have at most? Two? Three? Or just one?
Well, methods of a class often have no parameter at all. The explanation for this is that these always have an additional implicit “argument” available: this! The this pointer represents the context of execution. With the help of this, a member function
can access the attributes of its class, read, or manipulate them. In other words, from the perspective of a member function, attributes of a class feel like global variables.
When we think of a function in the pure mathematical sense (y = f(x)), it always has at least one parameter (see Chapter 7 about functional programming).
But why are too many parameters bad?
98
Chapter 4 Basics of Clean C++
First, every parameter in a function’s parameter list can lead to a dependency, with the exception of parameters of standard built-in types like int or double. If you use a complex type (e.g., a class) in a function’s parameter list, your code depends on that type. The header file containing the used type must be included.
Furthermore, every parameter must be processed somewhere inside of a function (if not, it is unnecessary and should be deleted immediately). Three parameters can lead to a relatively complex function, as we have seen by example of member function
BasicFrame::QueryFileName() from Apache’s OpenOffice.
In procedural programming it may sometimes be very difficult not to exceed
three parameters. In C, for instance, you will often see functions with more parameters. A deterrent example is the hopelessly antiquated Windows Win32 API, as shown in Listing 4-23.
Listing 4-23. The Win32 CreateWindowEx Function to Create Windows
HWND CreateWindowEx
(
DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle,
int x, int y,
int nWidth, int nHeight,
HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam
);
Well, this ugly code comes from ancient times, obviously. I’m pretty sure that if it were designed nowadays, the Windows API would not look like that any more. Not
without reason, there are numerous frameworks, such as Microsoft Foundation Classes
(MFC), Qt (https://www.qt.io), and wxWidgets (https://www.wxwidgets.org), that wrap this creepy interface and offer simpler and more object-oriented ways to create a graphical user interface (UI).
99

Chapter 4 Basics of Clean C++
And there are few possibilities to reduce the number of parameters. You could combine x, y, nWidth, and nHeight to a new structure named Rectangle, but then there are still nine parameters. An aggravating factor is that some of the parameters of this function are pointers to other complex structures, which for their part are composed of many attributes.
In good object-oriented designs, such long parameter lists are usually not required. But C++ is not a pure object-oriented language, such as Java or C#. In Java, everything must be embedded in a class, which sometimes leads to much boilerplate code. In C++ this is not required. You are allowed to implement free-standing functions in C++, that is, functions that are not members of a class. And that’s quite okay.
Tip Methods and functions should have as few parameters as possible. One parameter is the ideal number. Member functions (methods) of a class sometimes have no parameters at all. Usually those functions manipulate the internal state of the object, or they are used to query something from the object.
Avoid Flag Parameters
A flag parameter is a kind of parameter that tells a function to perform a different operation depending on its value. Flag parameters are mostly of type bool, and sometimes even an enumeration. See Listing 4-24.
Listing 4-24. A Flag Parameter to Control the Level of Detail on an Invoice
Invoice Billing::createInvoice(const BookingItems& items, const bool withDetails) {
if (withDetails) { //...
} else { //...
}
}
The basic problem with flag parameters is that you introduce two (or sometimes even more) paths through your function and hence increase its cyclomatic complexity.
100
Chapter 4 Basics of Clean C++
The value of such a parameter is typically evaluated somewhere inside the function in an if or switch/case statement. It is used to determine whether to take a certain action. It means that the function is not doing one thing exactly right, as it should be (see the section “One Thing, No More,” earlier in this chapter). It’s a case of weak cohesion (see Chapter 3) and violates the single responsibility principle (see Chapter 6 about object orientation).
And if you see the function call somewhere in the code, you do not know exactly what a true or false means without analyzing the Billing::createInvoice()function in detail. See Listing 4-25.
Listing 4-25. Baffling: What Does the True in the Argument List Mean?
Billing billing;
Invoice invoice = billing.createInvoice(bookingItems, true);
My advice is that you should simply avoid flag parameters. Such kinds of parameters are always necessary if the concern of performing an action is not separated from its configuration.
One solution could be to provide separate, well-named functions instead, as shown in Listing 4-26.
Listing 4-26. Easier to Comprehend: Two Member Functions with Intention- Revealing Names
Invoice Billing::createSimpleInvoice(const BookingItems& items) { //...
}
Invoice Billing::createInvoiceWithDetails(const BookingItems& items) { Invoice invoice = createSimpleInvoice(items);
//...add details to the invoice...
}
Another solution is a specialization hierarchy of billings, as shown in Listing 4-27.
101

Chapter 4 Basics of Clean C++
Listing 4-27. Different Levels of Details for Invoices, Realized the Object-Oriented Way
class Billing { public:
virtual Invoice createInvoice(const BookingItems& items) = 0; // ...
};
class SimpleBilling : public Billing { public:
Invoice createInvoice(const BookingItems& items) override; // ...
};
class DetailedBilling : public Billing { public:
Invoice createInvoice(const BookingItems& items) override; // ...
private:
SimpleBilling simpleBilling;
};
The private member variable of type SimpleBilling is required in the DetailedBilling class to be able to first perform a simple invoice creation without code duplication, and to add the details to the invoice afterward.
OVERRIDE SPECIFIER [C++11]
Since C++11, it can explicitly be specified that a virtual function should override a base class virtual function. For this purpose, the override identifier has been introduced.
If override appears immediately after the declaration of a member function, the compiler will check that the function is virtual and is overriding a virtual function from a base class. Thus, developers are protected from subtle errors that can arise when they merely think that they have overridden a virtual function, but in fact they have altered/added a new function, for example, due to a typo.
102