C++ Cheatsheet

C vs. C++ Functionality

C Interoperability

C++ is a superset of C, but some C functionality has been superceded and should not typically be used in C++ code. One exception to this rule is when interacting with C code, sometimes we are forced to use C functionality.

malloc and free

In C++, malloc and free should never be used. The new and delete operators should be used instead. These are type-safe, and they also ensure that the appropriate constructor and destructor is called.

Program Structure

Header Files

Header files (e.g. .h, .hpp) typically contain function declarations, while source files (e.g. .cpp, .cc) typically contain function definitions.

Source files can include header files in order to gain access to the declarations therein. After compilation, it is the responsibility of linker to supply the appropriate definition for each referenced declaration; a header can tell the compiler that a function exists, but the compiler does not know where it is defined.

In theory everything could be defined in header files but this would drastically increase the compile time.

Include Guards

Each symbol (function, variable, etc.) can only be defined once. Otherwise how can the linker know which version is the correct one?

This is problematic when definitions are present in header files, as the same header files may be included multiple times within a program.

The solution for this is to use include guards:

#ifndef MY_HEADER_FILE_H
#define MY_HEADER_FILE_H

// header file contents

#endif

Memory Management

Memory Management

The practice of relying on constructor and destructors for memory management is sometimes referred to as Resource Acquisition Is Initialization (RAII).

Memory leaks are still possible if we forget to call delete. One solution to this is to use smart pointers, which call new and delete automatically based on reference counting.

Heap vs. Stack

Using the new operator creates an object on the heap, but by default objects are created on the stack. Stack-allocated objects are automatically deleted (and their destructor called) when they go out of scope, making them much easier and safer to use.

Stack-allocated objects are also cheaper to create and access, since their memory address can be determined at compile time. However, the stack size is very limited compared to the heap, so it should not be used for large objects.

Basic Syntax & Keywords

Right-Left Rule

The right-left rule allows us to reliably interpret complex type declarations. It is defined as follows:

  1. Start reading the declaration from the identifier.
  2. Read to the right until a closing bracket is reached.
  3. Read to the left until the matching bracket is found.
  4. Repeat from step 2, starting outside the brackets, until the whole declaration has been parsed.

Example:

int * (* (*fp1) (int) ) [10];
Start from the variable name:                       -> fp1
Read right, hit a bracket; go left to find *        -> is a pointer
Read right again outside the brackets; find (int)   -> to a function that takes an int
Hit another bracket; go left to find *              -> and returns a pointer
Read right again outside the brackets; find [10]    -> to an array of 10
Read left to find *                                 -> pointers to
Keep reading left to find int                       -> ints

const

Variables, parameters and methods should be denoted as const wherever possible to prevent accidental modification. const methods of a class can only call other const methods.

This is generally appropriate for getter methods (unless they return a reference to a member variable, because that would enable modification of that object).

East / west const

There is ongoing debate about whether the const keyword should be placed to the left or to the right of a variable declaration. The former is arguably more natural, but the latter is more consistent.

constexpr

constexpr should be used for variables and functions whose values can be determined at compile time. These can then be used in the declarations of const variables.

noexcept

Functions that should never throw an exception can be marked noexcept. This is a useful form of documentation, and it can allow certain optimisations by the compiler. Should such a function throw an exception, the program will terminate.

inline

These days, inline is rarely needed, as the compiler generally knows when this kind of optimisation is appropriate. However, small functions defined in header files are good candidates for inline.

inline tells the compiler not to produce an error if a function definition is encountered multiple times, on the assumption that the function is defined in every translation unit where it is used, and that each definition is exactly the same.

Pointers & References

Pointer Declarations

Misleading Syntax

It is better to place the * (or &) next to the variable name, to prevent misleading declarations like this:

int* p, q;

In this example, only p is actually a pointer.

This ambiguity can be resolved with the help of a typedef. In this example, both p and q are pointers:

typedef int * IntPointer;
IntPointer p, q;

Pointers to Arrays

int tiles[32][4];
int (*p)[4] = tiles;    // p is a pointer to an array of 4 ints;
                        //  (`tiles` is equivalent to `tiles[0]`)
int *q[5];              // q is a pointer to an array of 5 ints

Function Pointers

Function pointers are declared using:

return_type (*variable_name)(function_params);

For example, here p is a pointer to a function that takes 2 floats and returns a pointer to a char:

char * (*p)(float, float);

Arrow (->) vs. Dot (.)

The arrow is used when accessing an object's members, when we have a pointer to the object.

This dereferences the pointer before accessing the member. Dereferencing a pointer means getting the value that is stored in the memory location that the pointer points to.

In other words, foo->bar() is the same as (*foo).bar().

Smart Pointers

TODO

TODO.

Collections

vector

TODO.

map

TODO.

Iterators

TODO.

Templates

TODO

TODO.

Classes

Constructors

TODO.

Destructors

TODO.

Copy Constructors

TODO.

Assignment Operators

TODO.

Rule of Five

TODO.

Inheritance

Virtual Destructors

TODO.

Pure Virtual

TODO.

Object Slicing

TODO.

Lambda Functions

TODO

TODO.

Credits

The following resources were used in the creation of this article:

Published 09/03/2021