Skip to content

Latest commit

 

History

History
1225 lines (772 loc) · 53.6 KB

patterns_and_idioms.md

File metadata and controls

1225 lines (772 loc) · 53.6 KB

Patterns, idioms, and design principles

Table of contents


Design principles

🔗

🎥

📖

Style and guidelines

🔗

🎥

Code organization

🔗

Naming and aliasing

🔗

🎥

Initialization

🔗

West-const vs east-const

🎥

Comments

🔗

🎥

Object-oriented design

🔗

🎥

📖

Liskov substitution principle

Liskov substitution principle states that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e. an object of type T may be substituted with any object of a subtype S) without altering any of the desirable properties of the program (correctness, task performed, etc.).

🔗

Interface segregation principle

The interface segregation principle states that no client should be forced to depend on methods it does not use.

Data-oriented design

🎥

Interface design

🔗

🎥

Function parameters

🔗

Function return values

:link

Destructors

🔗

🎥

Move semantics

See also Rvalue references, universal references, and move semantics – Core language.

:link

Rule of zero/three/five

The rule of zero: if a class requires no user-defined constructors, no user-defined assignment operators and no user-defined destructor, avoid defining them. The rule of three/five: if a class requires a user-defined destructor, a user-defined copy (and move) constructor, or a user-defined copy (and move) assignment operator, it almost certainly requires all three (five).

🔗

Contracts

🔗

🎥

Error handling

🔗

🎥

Exceptions

See also Exceptions – Core language and Exceptions – The standard library and proposals.

Levels of exception guarantee:

  • nothrow exception guarantee: the function never throws exceptions;
  • strong exception guarantee: if the function throws an exception, the state of the program is rolled back to the state just before the function call (commit-or-rollback semantics);
  • basic exception guarantee: if the function throws an exception, the program is in a valid state, no resources are leaked, and all objects’ invariants are intact;
  • no exception guarantee: if the function throws an exception, the program may not be in a valid state, resource leaks, memory corruption, or other invariant-destroying errors may have occurred.

For exceptions in destructors, see Destructors.

🔗

🎥

Testing

🔗

🎥

Seams

A seam is a place where a program behaviour can be altered without modifying it in that place.

🔗

Embedded systems

🎥


Safety and security

🎥

Memory safety

🎥

📄

Safe integers

🎥


Working with existing code and legacy systems

🎥


Overview of patterns and idioms

A design pattern is a general technique used to solve a class of related problems.

🔗

🎥


Creational patterns

🔗

Abstract factory

The abstract factory pattern provides an interface for creating related or dependent objects without specifying the objects’ concrete classes.

Builder

The builder pattern separates the creation of an object from its representation. It is used to create complex objects that are built in multiple stages:

struct Builder {
    Builder(...);
    Builder& add(...) { ...; return *this; }
    operator Product() const;
};

Product pr = Builder(...).add(...).add(...).add(...);

🔗

Factory method / Virtual constructor

Factory method pattern allows a class to defer instantiation to subclasses.

📖

In-place factory, typed in-place factory

🔗

Singleton

The singleton pattern ensures that a class has one instance and provides a global point of access to that instance. It is useful when exactly one object is needed to coordinate actions across the system.

class Singleton {
public:
    static Singleton& instance() {
        static Singleton inst;
        return inst;
    }

private:  // protected:
    Singleton();
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    ~Singleton() = default;
};

🔗

📖

Two-phase initialization / Functional update

🎥


Structural patterns

Adapter

🔗

🎥

Bridge and pimpl

🔗

🎥

Type erasure

🔗

🎥

Flyweight

🔗

Passkey

🔗

Copy-and-swap / Move-and-swap

The copy-and-swap idiom allows an assignment operator to be implemented elegantly with strong exception safety. However, it is often harmful to performance when a strong exception safety guarantee of the copy assignment operator is not needed.

class Object {
public:
    Object(const Object&);
    Object(Object&&);

    Object& operator=(const Object& obj) {
        Object{obj}.swap(*this);
        return *this;
    }

    Object& operator=(Object&& obj) {
        Object{std::move(obj)}.swap(*this);
        return *this;
    }
};

🔗

📖

Execute-around

🔗

Execute-around object (RAII)

Execute-around object idiom abstracts the execution of a pair of actions that surround a sequence of statements, or a single action that follows a sequence. This idiom is also known by the name of scope guard and resource acquisition is initialization (RAII). An example of this idiom is provided by standard library smart pointers, e.g. by std::unique_ptr.

🔗

🎥

📖

  • RAII – C++ reference

Execute-around proxy

Execute-around proxy idiom applies execute-around object idiom for individual calls on an object.

Execute-around pointer

Execute-around pointer idiom defines an execute-around proxy idiom when the actions performed on a target object are the same for all functions.

📝

  • This idiom can be used to “convert” a non-thread-safe class into a thread-safe one by automatically locking and unlocking a mutex when member functions are called.

🔗

Execute-around function

Execute-around function idiom safely groups and executes a sequence of statements that must be enclosed by a pair of actions, or followed by a single action.

Duff’s device

🔗

switch statement

🔗


Behavioural patterns

Chain-of-responsibility

The chain-of-responsibility is a design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain.

🔗

Strategy / Policy

The strategy pattern enables run- or compile-time selection of an algorithm for a particular behaviour. This pattern is also known by the name of policy pattern.

🎥

📖

Dependency injection

Dependency injection is a design pattern in which an object or function receives other objects or functions that it depends on. It aims to separate the concerns of constructing objects and using them, leading to loosely coupled programs.

🎥

Visitor

The visitor pattern separates an algorithm from an object structure, which is the data for this algorithm. It lets one define a new operation without changing the classes of the elements on which it operates.

class Visitor {
public:
    void visit(A&);
    void visit(B&);
};

class A {
public:
    void accept(Visitor& v) { v.visit(*this); }
};

class B {
public:
    void accept(Visitor& v) { v.visit(*this); }
};

🔗

🎥

Iterator

See also Iterators – The standard library and proposals.

🔗

Transform iterators

A transform iterator adapts an iterator by modifying the dereference operator to apply a function object to the result of dereferencing the iterator and returning the result.

🔗

Constant iterators

🔗

Iterator sentinels

🔗

Template method

Execution token

🎥

Observer


Miscellaneous patterns

Address of

The "address of" trick is used to find the address of an object of a class that has an overloaded unary operator&.

🔗

Address of – WikiBooks

const and non-const member functions

Opaque typedef (whole value)

An opaque type is a new type that is distinct from and distinguishable from its underlying type, yet retaining layout compatibility with its underlying type. The intent is to allow programmer control (1) over substitutability between an opaque alias and its underlying type, and (2) over overloading based on any parameter whose type is or involves an opaque alias.

template<class T> class Whole_value {
public:
    Whole_value(T v) : v_(v) {}
    operator T() const { return v_; }
private:
    T v_;
};

🔗

🎥

Infinite loop

for (;;) { ... }
while (true) { ... }
do { ... } while (true);

See also Infinite loops – Hardware, optimization, and OS internals.

🔗

Unused variables and return values

Compile-time strings

🔗

Customization point objects

🔗


Metaprogramming patterns

Curiously recurring template

The curiously recurring template pattern (CRTP) is an idiom in which a class X derives from a class template using X itself as template parameter:

template<class Derived>
class Base {
  private:
    friend Derived;
    Base();
  public:
    void foo() { static_cast<Derived*>(this)->foo(); }
};

class X : public Base<X> {
  public:
    void foo();
}

🔗

📖

Expression templates

📖

Barton–Nackman trick

🔗

📖

Shim class

🔗

  • D.Reichard. Shim classes – C/C++ Users Journal 18 (2000)

Integral constant wrapper / Int-to-type

🔗

📖


Lambda expression idioms

See also Lambda expressions – Core language.

🎥

Immediately invoked function expressions

const auto value = []{ return /* complex code */; }();
const auto value = std::invoke([]{ return /* complex code */; });

🔗

Lambda overload set

template<class... Ts>
struct overloaded : Ts... {
	using Ts::operator()...;
};

template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;

🔗

Recursive lambdas

🔗


Concurrency patterns

🎥

Double-checked locking

See also Multithreading – Concurrency and parallelism.

🔗

🎥


Functional programming patterns

🎥

Monads

See also Monads – Functional programming – General reviews and interviews.

🎥


Antipatterns

🔗

🎥

📖

Go to

📄

Standard library algorithms abuse

🔗

🎥

  • C.Hoekstra. Algorithm intuition. Part I, Part II – CppCon (2019)