C++
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 (regex, STL containers) thanks to the C++ standard library.
Some people may think of it as an object-oriented version of C.
Usage
How to do things using the C++ standard library (stdlib).
Compilation
g++
g++ my_driver.c [-Iincludefolder] -o my_program.out
Misc optimizations
-std=c++17
for C++17 support-O3
for level 3 optmizations
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
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
Casting
C++ has several types of casts. These are the main ones you should use.
static_cast
dynamic_cast
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 reinterpret_cast
.
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
#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.
Filesystem
#include <filesystem>
Convenient functions for filesystem. Added since C++17.
Path
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 tostring
Directories
- Notes
create_directory
requires that the parent directory already exists- If not, use
create_directories
instead
- If not, use
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::istream 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>
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
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 callshared_from_this()
withoutmake_shared
or if you do not publically inheritstd::enable_shared_from_this<T>
- Then you can call
- 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.
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
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_case<std::chrono::milliseconds>(end - start).count()
<< " ms" << std::endl;
Execution
#include <execution>
The execution header gives you tools for parallel execution.
See execution_policy_tag.
C++17 Parallel Algorithms blog.
- Parallel Sorting Example
std::sort(std::execution::par_unseq, sorted.begin(), sorted.end());
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, many of which are now built into the standard library (std) of C++.
This section focuses only on the portions of STL which have been incorporated into the C++ standard library.
Simple Containers
std::pair
Sequences
std::array
#include <array>
In C++, you can use std::vector
which gives you a resizable array.
This will allocate an array in the heap.
array vs vector
If you need a statically allocated array, you can use std::array
in the array
header.
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 std::vector
.
If you want to manually allocate an array on the heap, you can do so as follows:
auto my_arr = std::make_shared<std::array<char,64>>();
std::vector
Reference
Use vector for almost everything...
It is an ArrayList.
Note that vector<bool>
is not an array of bools.
This has several nuances so you should use vector<char>
instead.
// 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();
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
std::stack
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();
Associative Containers
Also known as maps or associative arrays.
std::unordered_set
#include <unordered_set>
This is a hashset.
std::unordered_set<int> my_set;
// add things to myset
my_set.insert(5);
// Check contains
my_set.find(5) != my_set.end();
std::unordered_map
- 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) {}
};
Boost
Programming Styles
Modern C++
- Use RAII principles.
- Use smart pointers instead of new and delete.
- Namely,
std::make_unique
andstd::shared_ptr
. - Or
std::vector<char>
if you just need some memory in the heap.
- Namely,
- Use clang-format.
Orthodox C++
Reference
Somewhat opposite of modern C++.
Also known as "C with Classes"
Basically only use C++ for its classes. Do everything else C-style.
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
cppreference raii
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 (destructor called), the resource is released.
In general, each RAII object should have all of the following:
- Constructor acquiring resources
- Copy Constructor
- Assignment operator
- Destructor releasing resources
- Swap function (for
std::swap
) - Move constructor (since C++11, for
std::move
)
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;
}
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);
}
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);
}
private:
std::size_t mSize;
int* mArray;
};
Useful Libraries
A list of useful libraries
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.