C++: Difference between revisions

From David's Wiki
 
(52 intermediate revisions by the same user not shown)
Line 1: Line 1:
__FORCETOC__
__FORCETOC__
C++ is a very popular and powerful language which includes all the low-level features of [[C_(programming_language) | C]] (e.g. pointers, operator overloading) along many high-level features (regex, STL containers) thanks to the C++ standard library.<br>
C++ is a very popular and powerful language which includes all the low-level features of [[C_(programming_language) | C]] (e.g. pointers, operator overloading) along many high-level features (RAII, STD algorithms, STL containers) thanks to the C++ standard library.
Some people may think of it as an object-oriented version of C.


==Usage==
==Usage==
Line 7: Line 6:
===Compilation===
===Compilation===
{{See also|CMake|Makefile}}
{{See also|CMake|Makefile}}
====cmake====
====g++====
====g++====
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
g++ my_driver.c [-Iincludefolder] -o my_program.out
g++ my_driver.c [-Iincludefolder] -o my_program.out
</syntaxhighlight>
</syntaxhighlight>
Misc optimizations
Standard optimizations
* <code>-std=c++17</code> for C++17 support
* <code>-std=c++17</code> for C++17 support
* <code>-O3</code> for level 3 optmizations
* <code>-O3</code> for level 3 optimizations
* <code>-g</code> to include debugging info
* <code>-march=native</code> - use all instructions available on the current CPU
* <code>-mtune=native</code> - optimize for the current CPU


===Syntax===
===Syntax===
====Main====
====Main====
All C++ programs launch in a <code>main</code> function.
All C++ programs launch in a <code>main</code> function.
Similar to [[C]], the arguments are <code>int argc</code> and <code>char *argv[]</code>.<br>
Similar to [[C (programming language) | C]], the arguments are <code>int argc</code> and <code>char *argv[]</code>.<br>
These can be easily converted to a <code>std::vector<std::string></code> for convenience.
These can be easily converted to a <code>std::vector<std::string></code> for convenience.


Line 44: Line 48:
[https://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used Types of casts]
[https://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used Types of casts]


C++ has several types of casts.
C++ has several types of casts including:
These are the main ones you should use.
* [https://en.cppreference.com/w/cpp/language/static_cast <code>static_cast</code>] - your standard cast with conversion. Does not perform any checks.
* <code>static_cast</code>
* [https://en.cppreference.com/w/cpp/language/dynamic_cast <code>dynamic_cast</code>] - for casting objects with checking, requires a polymorphic base class (with a virtual function). Will return nullptr.
* <code>dynamic_cast</code>
* [https://en.cppreference.com/w/cpp/language/reinterpret_cast <code>reinterpret_cast</code>] - cast without any conversion, for directly dealing with binary data, equivalent to <code>*(T*)</code> in C.
If you're casting between things but do not want to change the bit-pattern (e.g. binary data or pointers), you can also use <code>reinterpret_cast</code>.


====References====
====References====
Line 60: Line 63:
References are like pointers since they do not copy the object except they cannot be null and they cannot be reassigned.<br>
References are like pointers since they do not copy the object except they cannot be null and they cannot be reassigned.<br>
Note that primitives can also be used with references, in which case changes will propagate to the underlying value.<br>
Note that primitives can also be used with references, in which case changes will propagate to the underlying value.<br>
You can also use them as class attributes, initializing them in the constructor's initializer list.
You can also use them as class attributes, initializing them in the constructor's initializer list.<br>
To store references in a vector, you can use <code>std::reference_wrapper</code> and include the <code>functional</code> header.
 
====Types====
For simple programs, you can use the standard types:
* <code>int</code>, <code>uint</code>, <code>long</code>, <code>size_t</code>
* <code>float</code>, <code>double</code>
See [https://stackoverflow.com/questions/6462439/whats-the-difference-between-long-long-and-long SO] for the standard and guaranteed precision of these built-in types.
 
C++ also has fixed-width types in <code>#include <cstdint</code> (since C++11).<br>
[https://en.cppreference.com/w/cpp/header/cstdint cppreference cstdint]<br>
I recommend using these for anything with specific or high precision requirements.<br>
Typically, I use:
* <code>uint8_t</code> instead of <code>char</code> or <code>std::byte</code>.<br>
* <code>int64_t</code> instead of <code>long long</code>


===String===
===String===
Line 102: Line 119:
Strings are mutable in C++.<br>
Strings are mutable in C++.<br>
I typically use <code>+</code> or <code>ostringstream</code> to build strings.
I typically use <code>+</code> or <code>ostringstream</code> to build strings.
====std::basic_string_view====
[https://en.cppreference.com/w/cpp/string/basic_string_view std::basic_string_view]
This is useful for writing functions which accept anything that looks like a string such as substrings, since typically <code>std::string::substr</code> performs a copy.
Note that <code>std::string_view</code> is <code>std::basic_string_view<char></code>.


===Filesystem===
===Filesystem===
Line 151: Line 175:


int main() {
int main() {
   std::istream my_file("my_file.txt");
   std::ifstream my_file("my_file.txt");
   std::string line;
   std::string line;
   // Read line by line
   // Read line by line
Line 270: Line 294:
** May throw <code>bad_weak_ptr</code> if you call <code>shared_from_this()</code> without <code>make_shared</code> or if you do not publically inherit <code>std::enable_shared_from_this<T></code>
** May throw <code>bad_weak_ptr</code> if you call <code>shared_from_this()</code> without <code>make_shared</code> or if you do not publically inherit <code>std::enable_shared_from_this<T></code>
* When writing functions when do not operate on pointers and do not claim ownership of objects, you should just take a reference to the object as the argument.
* When writing functions when do not operate on pointers and do not claim ownership of objects, you should just take a reference to the object as the argument.
* <code>std::auto_ptr</code> was a predecessor to <code>std::unique_ptr</code> which allowed copies. It shouldn't be used anymore.


====Garbage Collection====
====Garbage Collection====
Line 399: Line 424:
auto end = std::chrono::high_resolution_clock::now();
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Time elapsed: "  
std::cout << "Time elapsed: "  
           << std::chrono::duration_case<std::chrono::milliseconds>(end - start).count()  
           << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()  
           << " ms" << std::endl;
           << " ms" << std::endl;
</syntaxhighlight>
</syntaxhighlight>
Line 405: Line 430:
===Execution===
===Execution===
<code>#include <execution></code><br>
<code>#include <execution></code><br>
The execution header gives you tools for parallel execution.<br>
The execution header gives you tools for parallel execution (since C++17).<br>
See [https://en.cppreference.com/w/cpp/algorithm/execution_policy_tag_t execution_policy_tag].<br>
See [https://en.cppreference.com/w/cpp/algorithm/execution_policy_tag_t execution_policy_tag].<br>
[https://devblogs.microsoft.com/cppblog/using-c17-parallel-algorithms-for-better-performance/ C++17 Parallel Algorithms blog].<br>
[https://devblogs.microsoft.com/cppblog/using-c17-parallel-algorithms-for-better-performance/ C++17 Parallel Algorithms blog].<br>
[https://developer.nvidia.com/blog/accelerating-standard-c-with-gpus-using-stdpar/ Nvidia Accelerating Standard C++ with GPUs Using stdpar]<br>
;Parallel Sorting Example
;Parallel Sorting Example
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
std::sort(std::execution::par_unseq, sorted.begin(), sorted.end());
std::sort(std::execution::par_unseq, sorted.begin(), sorted.end());
</syntaxhighlight>
</syntaxhighlight>
* <code>std::execution::seq</code> sequential
* <code>std::execution::unseq</code> vectorized only (C++20)
* <code>std::execution::par</code> parallel
* <code>std::execution::par_unseq</code> parallel and vectorized


===Random===
===Random===
Line 435: Line 467:
===Simple Containers===
===Simple Containers===
====std::pair====
====std::pair====
[https://en.cppreference.com/w/cpp/utility/pair std::pair]


===Sequences===
===Sequences===
====std::array====
====std::array====
<code>#include <array></code><br>
<code>#include <array></code><br>
In C++, you can use <code>std::vector</code> which gives you a resizable array.
This wrapper around C-style arrays gives us size information and allows the array to be passed around by reference while keeping the array on the stack or in a struct.
This will allocate an array in the heap.<br>
Unless you need stack allocation or allocation into a struct, you are should probably use a vector.
 
[https://shendrick.net/Coding%20Tips/2015/03/15/cpparrayvsvector.html array vs vector]<br>
If you need a statically allocated array, you can use <code>std::array</code> in the <code>array</code> header.<br>
This wrapper around C-style arrays gives us size information and allows the array to be passed around by reference while keeping the array on the stack unlike <code>std::vector</code>.<br>
If you want to manually allocate an array on the heap, you can do so as follows:
<syntaxhighlight lang="C++">
auto my_arr = std::make_shared<std::array<char,64>>();
</syntaxhighlight>


====std::vector====
====std::vector====
[https://en.cppreference.com/w/cpp/container/vector Reference]<br>
<code>#include <vector></code>
Use vector for almost everything...<br>
https://en.cppreference.com/w/cpp/container/vector<br>
It is an ArrayList.<br>
This is a dynamically-allocated resizable array, known as an ArrayList in Java.<br>
Note that <code>vector<bool></code> is not an array of bools.<br>
This has several nuances so you should use <code>vector<char></code> instead.<br>
<syntaxhighlight lang="c++">
<syntaxhighlight lang="c++">
// Basics
// Basics
Line 478: Line 501:
my_vec.back();
my_vec.back();
</syntaxhighlight>
</syntaxhighlight>
Note that [https://en.cppreference.com/w/cpp/container/vector_bool <code>vector<bool></code>] is a special case of bit-packed booleans instead of an array of bools. You should use <code>vector<char></code> instead if your code relies on it being continguous.<br>
====std::span====
<code>#include &lt;span&gt;</code><br>
https://en.cppreference.com/w/cpp/container/span<br>
This is view of some contiguous amount of memory. If the size is static, this is equivalent to a single pointer, otherwise is it equivalent to two pointers (i.e. begin and end).
If you use this as the parameter to your function, it will accept both arrays and vectors.
Additionaly, there is a [https://en.cppreference.com/w/cpp/container/span/subspan subspan] function so you don't need to pass around indices or pointers to get subvectors.


====std::deque====
====std::deque====
Line 497: Line 529:
[https://en.cppreference.com/w/cpp/container/queue Reference]<br>
[https://en.cppreference.com/w/cpp/container/queue Reference]<br>
<syntaxhighlight lang="c++">
<syntaxhighlight lang="c++">
std::queue<int> my_queue;


my_queue.push(a);
auto val = my_queue.front();
my_queue.pop(); // returns void
</syntaxhighlight>
</syntaxhighlight>


Line 519: Line 555:
my_stack.pop();
my_stack.pop();
</syntaxhighlight>
</syntaxhighlight>
====std::priority_queue====
This is a min/max heap.


===Associative Containers===
===Associative Containers===
Also known as maps or associative arrays.
Also known as maps or associative arrays.
====std::set====
[https://en.cppreference.com/w/cpp/container/set reference]<br>
<code>#include<set></code><br>
This is a binary tree (likely red-black tree). You can assume <math>O(\log n)</math> operations.
====std::map====
[https://en.cppreference.com/w/cpp/container/map reference]<br>
<code>#include<map></code><br>
This is a binary tree (likely red-black tree). You can assume <math>O(\log n)</math> operations.
====std::unordered_set====
====std::unordered_set====
[https://en.cppreference.com/w/cpp/container/unordered_set reference]<br>
<code>#include <unordered_set></code><br>
<code>#include <unordered_set></code><br>
This is a hashset.<br>
This is a hashset. You can assume operations are <math>O(1)</math> on average and <math>O(N)</math> worst case.<br>
<syntaxhighlight lang="cpp>
<syntaxhighlight lang="cpp>
std::unordered_set<int> my_set;
std::unordered_set<int> my_set;
// add things to myset
// Add
my_set.insert(5);
my_set.insert(5);
// Check contains
// Check contains
my_set.find(5) != my_set.end();
my_set.find(5) != my_set.end(); // Before C++20
my_set.contains(5); // C++20
// Remove
my_set.erase(5);
</syntaxhighlight>
</syntaxhighlight>
====std::unordered_map====
====std::unordered_map====
[https://en.cppreference.com/w/cpp/container/unordered_map reference]<br>
<code>#include<unordered_map></code><br>
This is a hashmap. You can assume operations are <math>O(1)</math> on average and <math>O(N)</math> worst case.<br>
<syntaxhighlight lang="C++">
std::unordered_map<int, std::string> my_map;
my_map[5] = "hey"; // Fine as long as value type is not a reference.
my_map.insert({5, "hey"}); // Necessary if value type is a reference.
my_map.find(5) != my_map.end();
my_map.contains(5); // C++20
</syntaxhighlight>
;Custom Keys
;Custom Keys
How to use a rational number as a key in C++
How to use a rational number as a key in C++
Line 549: Line 614:
</syntaxhighlight>
</syntaxhighlight>


==Boost==
==Functional Programming==
https://medium.com/swlh/doing-it-the-functional-way-in-c-5c392bbdd46a


Many of these can be parallelized with [https://en.cppreference.com/w/cpp/algorithm/execution_policy_tag_t execution policies] such as <code>std::execution::par</code> and <code>std::execution::par_unseq</code>. Paired with [https://adaptivecpp.github.io/AdaptiveCpp/stdpar/ AdaptiveCPP], some operations can be automatically GPU accelerated as well.
Most of these require C++20.
===Map===
* <code>std::for_each</code>
* <code>std::transform</code>
* [https://en.cppreference.com/w/cpp/algorithm/copy <code>std::copy</code>, <code>std::copy_if</code>]
* [https://en.cppreference.com/w/cpp/algorithm/fill <code>std::fill</code>]
===Reduce/Fold===
* <code>std::reduce</code>
* <code>std::accumulate</code>
* [https://en.cppreference.com/w/cpp/algorithm/ranges/fold_left <code>std::ranges::fold_left</code>] (C++23)
===Filter===
* <code>std::copy_if</code>
* <code>std::remove_if</code>
* <code>std::find_if</code>


==Programming Styles==
==Programming Styles==
Line 556: Line 641:
[https://github.com/rigtorp/awesome-modern-cpp List of resources]<br>
[https://github.com/rigtorp/awesome-modern-cpp List of resources]<br>
* Use RAII principles.
* Use RAII principles.
* Use smart pointers instead of new and delete.
** I.e. each object should manage it's own memory rather than the caller having to manage it.
** Namely, <code>std::make_unique</code> and <code>std::shared_ptr</code>.
** You should never use `malloc` and `free` unless interfacing with C libraries.
** Or <code>std::vector<char></code> if you just need some memory in the heap.
* Avoid the use of new and delete, instead using vector or smart pointers.
* Use clang-format.
* Use clang-format.


Line 564: Line 649:
* [https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md CppCoreGuidelines]
* [https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md CppCoreGuidelines]
* [https://google.github.io/styleguide/cppguide.html Google C++ Style Guide] - note that some people dislike this since it is focused on interoperability and suggests avoiding exceptions.
* [https://google.github.io/styleguide/cppguide.html Google C++ Style Guide] - note that some people dislike this since it is focused on interoperability and suggests avoiding exceptions.
===Orthodox C++===
[https://gist.github.com/bkaradzic/2e39896bc7d8c34e042b Reference]<br>
Somewhat opposite of modern C++.<br>
Also known as "C with Classes"<br>
Basically only use C++ for its classes. Do everything else C-style.<br>
The main benefit is compatibility with older compilers/libraries and easier understanding for people less familiar with newer C++ features.
* Don't use C++ runtime wrapper for C runtime includes (<cstdio>, <cmath>, etc.), use C runtime instead (<stdio.h>, <math.h>, etc.)
* Don't use stream (<iostream>, <stringstream>, etc.), use printf style functions instead.
* Don't use anything from STL that allocates memory, unless you don't care about memory management.
* Don't use exceptions.
* Don't use RTTI.


==RAII==
==RAII==
[https://en.cppreference.com/w/cpp/language/raii cppreference raii]<br>
[https://en.cppreference.com/w/cpp/language/raii cppreference raii]<br>
[https://en.cppreference.com/w/cpp/language/rule_of_three cppreference rule_of_three]<br>
Resource Acquisition Is Initialization - binds the life cycle of a resource to the lifetime of an object.<br>
Resource Acquisition Is Initialization - binds the life cycle of a resource to the lifetime of an object.<br>
For instance, the resource for a vector is an allocated amount of memory. Once the vector is destroyed (destructor called), the resource is released.<br>
For instance, the resource for a vector is an allocated amount of memory. Once the vector is destroyed and the destructor called, the resource is released.<br>
In general, each RAII object should have all of the following:
 
* Constructor acquiring resources
If you need any from one of the rules, you need to implement the remainder
* Copy Constructor
 
* Assignment operator
;Rule of zero<br>
* Destructor releasing resources
Do not use a custom deconstructor, copy constructor, or copy assignment. Push all of these operations into the classes of member variables such as <code>std::vector</code> and <code>unique_ptr</code>. This is the best and simplest case.
* Swap function (for <code>std::swap</code>)
 
* Move constructor (since C++11, for <code>std::move</code>)
;[[Wikipedia: Rule of three (C++ programming) | Rule of three]]
{{hidden | Example RAII Class |
* Destructor
* Copy constructor
* Copy assignment operator
 
;[[Wikipedia: Rule of three (C++ programming)#Rule of five | Rule of five]]
* All from rule of three plus:
* Move constructor
* Move operator
 
;Rule of four and a half:
* Destructor
* Copy constructor
* Copy-and-swap assignment operator
* Swap function
 
{{hidden | Example Rule of Four RAII Class |
Copied from [https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom stack overflow]
Copied from [https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom stack overflow]
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
Line 621: Line 710:
     }
     }


    // assignment operator
    dumb_array& operator=(dumb_array other) // (1)
    {
        swap(*this, other); // (2)
        return *this;
    }
    // swap
     friend void swap(dumb_array& first, dumb_array& second) // nothrow
     friend void swap(dumb_array& first, dumb_array& second) // nothrow
     {
     {
Line 630: Line 728:
         swap(first.mSize, second.mSize);
         swap(first.mSize, second.mSize);
         swap(first.mArray, second.mArray);
         swap(first.mArray, second.mArray);
    }
    dumb_array& operator=(dumb_array other) // (1)
    {
        swap(*this, other); // (2)
        return *this;
    }
    dumb_array(dumb_array&& other) noexcept ††
        : dumb_array() // initialize via default constructor, C++11 only
    {
        swap(*this, other);
     }
     }


Line 654: Line 739:
==Useful Libraries==
==Useful Libraries==
A list of useful libraries
A list of useful libraries
===Boost===
{{main | Boost (C++ libraries)}}
A set of popular C++ libraries. Most are header-only.
===cxxopts===
===cxxopts===
[https://github.com/jarro2783/cxxopts Link]<br>
[https://github.com/jarro2783/cxxopts Link]<br>
Line 661: Line 751:
{{main | Eigen (C++ library)}}
{{main | Eigen (C++ library)}}
A header-only C++ linear algebra library.
A header-only C++ linear algebra library.
===absl===
https://github.com/abseil/abseil-cpp is a library used by Google which supplements the standard library.


==References==
==References==
[[Category:Programming languages]]

Latest revision as of 03:13, 22 August 2024

C++ is a very popular and powerful language which includes all the low-level features of C (e.g. pointers, operator overloading) along many high-level features (RAII, STD algorithms, STL containers) thanks to the C++ standard library.

Usage

How to do things using the C++ standard library (stdlib).

Compilation

cmake

g++

g++ my_driver.c [-Iincludefolder] -o my_program.out

Standard optimizations

  • -std=c++17 for C++17 support
  • -O3 for level 3 optimizations
  • -g to include debugging info
  • -march=native - use all instructions available on the current CPU
  • -mtune=native - optimize for the current CPU

Syntax

Main

All C++ programs launch in a main function. Similar to C, the arguments are int argc and char *argv[].
These can be easily converted to a std::vector<std::string> for convenience.

#include <string>
#include <vector>
int main(int argc, char *argv[]) {
  std::vector<std::string> args(argv, argv + argc);
  // Your code here
  return EXIT_SUCCESS;
}

Headers

Reference

C++ includes C-headers such as math.h and cmath.
The C-style header will place everything in the global namespace while the C++ header will place everything in std.
You should use cmath.

Lambda Expressions

Reference

Casting

Types of casts

C++ has several types of casts including:

  • static_cast - your standard cast with conversion. Does not perform any checks.
  • dynamic_cast - for casting objects with checking, requires a polymorphic base class (with a virtual function). Will return nullptr.
  • reinterpret_cast - cast without any conversion, for directly dealing with binary data, equivalent to *(T*) in C.

References

References are accepted or store using &.
For example:

void healPerson(Person &person) {
  person.health = 100;
}

References are like pointers since they do not copy the object except they cannot be null and they cannot be reassigned.
Note that primitives can also be used with references, in which case changes will propagate to the underlying value.
You can also use them as class attributes, initializing them in the constructor's initializer list.
To store references in a vector, you can use std::reference_wrapper and include the functional header.

Types

For simple programs, you can use the standard types:

  • int, uint, long, size_t
  • float, double

See SO for the standard and guaranteed precision of these built-in types.

C++ also has fixed-width types in #include <cstdint (since C++11).
cppreference cstdint
I recommend using these for anything with specific or high precision requirements.
Typically, I use:

  • uint8_t instead of char or std::byte.
  • int64_t instead of long long

String

#include <string>

// c-str to string
char *old_string = "my c-style string";
string cpp_string(old_string);

// string to c-str
cpp_string.c_str();

// char to string
char my_char = 'a';
string my_str(1, my_char);

String Interpolation

Reference

#include <iostream>
#include <sstream>
#include <string>

int main() {
    std::string a = "a", b = "b", c = "c";
    // apply formatting
    std::stringstream s;
    s << a << " " << b << " > " << c;
    // assign to std::string
    std::string str = s.str();
    std::cout << str << "\n";
}

Buildings Strings

The Complete Guide to Building Strings In C++
There are multiple ways of buildings strings in C++.
Strings are mutable in C++.
I typically use + or ostringstream to build strings.

std::basic_string_view

std::basic_string_view

This is useful for writing functions which accept anything that looks like a string such as substrings, since typically std::string::substr performs a copy.

Note that std::string_view is std::basic_string_view<char>.

Filesystem

#include <filesystem>
Convenient functions for filesystem. Added since C++17.

Path

cppreference

Note if you use g++ <= version 9, you will need to add the flag -lstdc++fs.

using std::filesystem::path;

// Initialization
path my_path = "my_dir/my_file";
// or my_path = path("my_dir") / "my_file";

// Append to path
path("foo") / "bar"; // path("foo/bar")
path("foo") / "/bar"; // path("/bar")

// Print
std::cout << my_path << std::endl; // prints "my_dir/my_file" with quotes
std::cout << my_path.string() << std::endl; // prints my_dir/my_file without quotes
Notes
  • path supports implicit conversion to string


Directories

Notes
  • create_directory requires that the parent directory already exists
    • If not, use create_directories instead

Fstream

#include <fstream>
Used for input/output of files

Reading and Writing

Reading and writing is done using fstream.
If you don't need r/w, use istream for reading or ostream for writing.

#include <iostream>
#include <fstream>

int main() {
  std::ifstream my_file("my_file.txt");
  std::string line;
  // Read line by line
  // You can also read using <<
  while (getline(my_file, line)) {
    std::cout << line << std::endl;
  }
  return 0;
}

Reading a whole file

Reference and comparison of different methods

#include <fstream>
#include <string>
#include <cerrno>

std::string get_file_contents(const std::string &filename)
{
  std::ifstream in(filename, std::ios::in | std::ios::binary);
  if (in.good())
  {
    std::string contents;
    in.seekg(0, std::ios::end);
    contents.resize(static_cast<unsigned int>(in.tellg()));
    in.seekg(0, std::ios::beg);
    in.read(&contents[0], contents.size());
    return contents;
  }
  std::cerr << "Failed to open file: " << filename << std::endl;
  throw(errno);
}

Regular Expressions

#include <regex>

Reference


Thread

#include <thread>
std::thread reference

Basic Usage:

std::thread my_thread(thread_function);
// Calling methods
// You can also pass in parameters as usual
std::thread my_thread(&Class::method, this));
// Lambda functions
std::thread my_thread([&]() {
 // do something
});

// Wait for thread to finish
my_thread.join();

// get id of thread
std::thread::id my_id = my_thread.get_id();

// get id of this thread
std::thread::id my_id = std::this_thread::get_id();

Sleep

std::this_thread::sleep_for(std::chrono::milliseconds(1));

Parallel For

Reference

Memory

#include <memory>

Smart Pointers

Smart Pointers
Smart pointers were added in C++11.
There are 3 types of smart pointers:

  • unique_ptr
  • shared_ptr
  • weak_ptr

Use unique_ptr when one piece of code owns the memory at any given time.
Use shared_ptr when multiple objects need to reference the same thing.
Use weak_ptr to avoid cyclic dependencies which cause issues with reference counting.
If you are using C++14 or newer, you should use make_unique or make_shared which will only make one memory allocation for both the object and the pointer rather than two memory allocations.
Alternatively if you already have a smart pointer, you can call my_ptr.reset(new Car()) to change the pointer or my_ptr.reset() to deallocate the object referenced by the pointer. Example:

// Block-scope car
Car my_car;

// Old C++
// Must call delete my_car; to avoid memory leaks.
Car *my_car = new Car();

// Using unique ptr
std::unique_ptr<Car> my_car(new Car());

// Or starting from C++14
auto my_car = std::make_unique<Car>();
Notes
  • If the object you need is not very large, you can consider just including it as part of your class (or leaving it on the stack) rather than use pointers.
  • If you want to get a copy of the smart pointer to the current object, the object must publically inherit std::enable_shared_from_this<T>
    • Then you can call shared_from_this() from within any method (not the constructor).
    • May throw bad_weak_ptr if you call shared_from_this() without make_shared or if you do not publically inherit std::enable_shared_from_this<T>
  • When writing functions when do not operate on pointers and do not claim ownership of objects, you should just take a reference to the object as the argument.
  • std::auto_ptr was a predecessor to std::unique_ptr which allowed copies. It shouldn't be used anymore.

Garbage Collection

Starting from C++11, you should use smart pointers such as shared_ptr which have automatic garbage collection.

Traditional C++ does not have garbage collection.
After using new to allocate an object, use delete to deallocate it.
You can also use C allocation with malloc, calloc, alloca, and free, though it is not recommended since these are not type-safe.

Custom Deleter

Custom Deleters
When using smart pointers, the default deleter is the delete function but you can also specify your own deleter.


# Using a functor
struct AVFrameDeleter {
  void operator()(AVFrame *p) { av_frame_free(&p); }
};
std::unique_ptr<AVFrame, AVFrameDeleter> rgb_frame(av_frame_alloc());

# Using free
std::unique_ptr<void *, decltype(std::free) *> my_buffer(std::malloc(10), std::free);


Deallocate

Normally, containers such as std::vector will automatically deallocate memory from the heap when the destructor is called. However, occationally you may want to coerse this deallocation yourself.
There are a few ways to do this:

  • Use smart pointers
  • Swap
  • Call a clear/shrink/deallocate function

Example Reference:

// Using smart pointers
std::unique_ptr<std::vector<float>> my_vector = make_unique<std::vector<float>>(99);
my_vector.reset();

// Swap
std::vector<float> my_vector(99);
my_vector = std::vector<float>;
// Or alternatively
// std::vector<float>().swap(my_vector);
// std::swap(my_vector, std::vector<float>);

// Swap for cl::Buffer
cl::Buffer my_buf(context, CL_MEM_READ_WRITE, size);
my_buf = cl::Buffer();

// Clear and shrink
// Specific to std::vector
std::vector<float> my_vector(99);
my_vector.clear();
my_vector.shrink_to_fit();

Limits

#include <limits>
Reference
C++ has standard macros such as INT_MAX.
The limits header adds these limits for every type.

// Equivalent to FLT_MAX
std::numeric_limits<float>::max();

Utility

#include <utility>

std::move

Ref
Use std::move to move containers.

Algorithm

std::find

Reference

std::generate

cppreference
Allows you to fill a container using a function call

#include <random>
#include <iostream>
#include <algorithm>

int main()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    # Fill with integers in [0, 10]
    std::uniform_int_distribution<> dis(0, 10);

    std::vector<int> my_vec(10, 0);
    std::generate(my_vec.begin(), my_vec.end(), [&](){return dis(gen);});
  <br />
    for (int v : my_vec) {
        std::cout << v << " ";
    }
    std::cout << std::endl;

    return 0;
}

Numeric

std::iota

Reference
Fills an array or vector with increasing values. Can pass in a starting number.

std::vector<int> v(60);
std::iota(v.begin(), v.end(), 0);


std::accumulate

Adds up numbers. Can pass in a starting number.

std::vector<int> v(60);
std::iota(v.begin(), v.end(), 0);
std::accumulate(v.begin(), v.end(), 0);

Chrono

#include <chrono>
Lots of useful time stuff. Good for timing your code.

auto start = std::chrono::high_resolution_clock::now();
// do something
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Time elapsed: " 
          << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() 
          << " ms" << std::endl;

Execution

#include <execution>
The execution header gives you tools for parallel execution (since C++17).
See execution_policy_tag.
C++17 Parallel Algorithms blog.
Nvidia Accelerating Standard C++ with GPUs Using stdpar

Parallel Sorting Example
std::sort(std::execution::par_unseq, sorted.begin(), sorted.end());
  • std::execution::seq sequential
  • std::execution::unseq vectorized only (C++20)
  • std::execution::par parallel
  • std::execution::par_unseq parallel and vectorized

Random

#include <random>
cppreference.com

std::random_device rd;  //Will be used to obtain a seed for the random number engine
std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
std::uniform_int_distribution<> dis(1, 6);
 
for (int n=0; n<10; ++n)
   //Use dis to transform the random unsigned int generated by gen into an int in [1, 6]
   std::cout << dis(gen) << ' ';
std::cout << '\n';

STL

STL is the Standard Template Library originally implemented in 1994 by Stepanov and Lee from HP.
STL consists of a general set of algorithms, containers, functions, and iterators.
Today, STL refers to those containers and algorithms which are now built into the standard library (std) of C++.

Simple Containers

std::pair

std::pair

Sequences

std::array

#include <array>
This wrapper around C-style arrays gives us size information and allows the array to be passed around by reference while keeping the array on the stack or in a struct. Unless you need stack allocation or allocation into a struct, you are should probably use a vector.

std::vector

#include <vector> https://en.cppreference.com/w/cpp/container/vector
This is a dynamically-allocated resizable array, known as an ArrayList in Java.

// Basics
vector my_vec;
// Vector with size 5
vector my_vec(5);
// Vector with size 5 initialized to 1
vector my_vec(5, 1);

// Length of vector
my_vec.size();

// Equivalent to size()==0
my_vec.empty();

// Equivalent to my_vec[0];
// Undefined on empty vectors
my_vec.front();

// Equivalent to my_vec[my_vec.size()-1];
// Undefined on empty vectors
my_vec.back();

Note that vector<bool> is a special case of bit-packed booleans instead of an array of bools. You should use vector<char> instead if your code relies on it being continguous.

std::span

#include <span>
https://en.cppreference.com/w/cpp/container/span
This is view of some contiguous amount of memory. If the size is static, this is equivalent to a single pointer, otherwise is it equivalent to two pointers (i.e. begin and end). If you use this as the parameter to your function, it will accept both arrays and vectors. Additionaly, there is a subspan function so you don't need to pass around indices or pointers to get subvectors.

std::deque

Double-ended queue

std::list

This is a doubly linked list. You can delete elements from the middle of the list if you know have an iterator.

list<int> m_list;

list<int>::iterator m_it = m_list.insert(5);

// Remove the element
m_list.erase(m_it);

Container adaptors

std::queue

Reference

std::queue<int> my_queue;

my_queue.push(a);
auto val = my_queue.front();
my_queue.pop(); // returns void

std::stack

cppreference

std::stack<char> my_stack;

// Push to stack
// You can also use emplace
// Returns void
my_stack.push('a');

// Peek
// Always make sure stack is not empty
char top = my_stack.top('a');

// Pop
// Note: returns void
// Always make sure stack is not empty
my_stack.pop();

std::priority_queue

This is a min/max heap.

Associative Containers

Also known as maps or associative arrays.

std::set

reference
#include<set>
This is a binary tree (likely red-black tree). You can assume \(\displaystyle O(\log n)\) operations.


std::map

reference
#include<map>
This is a binary tree (likely red-black tree). You can assume \(\displaystyle O(\log n)\) operations.

std::unordered_set

reference
#include <unordered_set>
This is a hashset. You can assume operations are \(\displaystyle O(1)\) on average and \(\displaystyle O(N)\) worst case.

std::unordered_set<int> my_set;
// Add
my_set.insert(5);
// Check contains
my_set.find(5) != my_set.end(); // Before C++20
my_set.contains(5); // C++20
// Remove
my_set.erase(5);

std::unordered_map

reference
#include<unordered_map>
This is a hashmap. You can assume operations are \(\displaystyle O(1)\) on average and \(\displaystyle O(N)\) worst case.

std::unordered_map<int, std::string> my_map;
my_map[5] = "hey"; // Fine as long as value type is not a reference.
my_map.insert({5, "hey"}); // Necessary if value type is a reference.
my_map.find(5) != my_map.end();
my_map.contains(5); // C++20
Custom Keys

How to use a rational number as a key in C++

struct Fraction
{
    int num;
    int den;

    bool operator==(const Fraction &other) const { 
        return num*other.den == den * other.num;
    }

    Fraction(int a, int b) : num(a), den(b) {}
};

Functional Programming

https://medium.com/swlh/doing-it-the-functional-way-in-c-5c392bbdd46a

Many of these can be parallelized with execution policies such as std::execution::par and std::execution::par_unseq. Paired with AdaptiveCPP, some operations can be automatically GPU accelerated as well.

Most of these require C++20.

Map

Reduce/Fold

Filter

  • std::copy_if
  • std::remove_if
  • std::find_if

Programming Styles

Modern C++

List of resources

  • Use RAII principles.
    • I.e. each object should manage it's own memory rather than the caller having to manage it.
    • You should never use `malloc` and `free` unless interfacing with C libraries.
  • Avoid the use of new and delete, instead using vector or smart pointers.
  • Use clang-format.
Resources

RAII

cppreference raii
cppreference rule_of_three
Resource Acquisition Is Initialization - binds the life cycle of a resource to the lifetime of an object.
For instance, the resource for a vector is an allocated amount of memory. Once the vector is destroyed and the destructor called, the resource is released.

If you need any from one of the rules, you need to implement the remainder

Rule of zero

Do not use a custom deconstructor, copy constructor, or copy assignment. Push all of these operations into the classes of member variables such as std::vector and unique_ptr. This is the best and simplest case.

Rule of three
  • Destructor
  • Copy constructor
  • Copy assignment operator
Rule of five
  • All from rule of three plus:
  • Move constructor
  • Move operator
Rule of four and a half
  • Destructor
  • Copy constructor
  • Copy-and-swap assignment operator
  • Swap function
Example Rule of Four RAII Class

Copied from stack overflow

#include <algorithm> // std::copy
#include <cstddef> // std::size_t

class dumb_array
{
public:
    // (default) constructor
    dumb_array(std::size_t size = 0)
        : mSize(size),
          mArray(mSize ? new int[mSize]() : nullptr)
    {
    }

    // copy-constructor
    dumb_array(const dumb_array& other)
        : mSize(other.mSize),
          mArray(mSize ? new int[mSize] : nullptr),
    {
        // note that this is non-throwing, because of the data
        // types being used; more attention to detail with regards
        // to exceptions must be given in a more general case, however
        std::copy(other.mArray, other.mArray + mSize, mArray);
    }

    // destructor
    ~dumb_array()
    {
        delete [] mArray;
    }

    // assignment operator
    dumb_array& operator=(dumb_array other) // (1)
    {
        swap(*this, other); // (2)

        return *this;
    }

    // swap
    friend void swap(dumb_array& first, dumb_array& second) // nothrow
    {
        // enable ADL (not necessary in our case, but good practice)
        using std::swap;

        // by swapping the members of two objects,
        // the two objects are effectively swapped
        swap(first.mSize, second.mSize);
        swap(first.mArray, second.mArray);
    }

private:
    std::size_t mSize;
    int* mArray;
};

Useful Libraries

A list of useful libraries

Boost

A set of popular C++ libraries. Most are header-only.

cxxopts

Link
A header-only C++ argument parser.
Note that if you already use Boost, you can use Boost::Program_options instead.

Eigen

A header-only C++ linear algebra library.

absl

https://github.com/abseil/abseil-cpp is a library used by Google which supplements the standard library.

References