Hello World: Overloading Operator and Namespace

Here is an example of overloading >> and << operator so that we can print out own class out to console. Note that the std::cout << "Hello World" << std::endl is overloaded by default.

std::cout << "Hello World" will be called first and then the return value will be called with << std::endl. std::endl has two functions: (1) make the cursor on the next line. (2) make sure flush to console immediately.

#include <iostream>
using namespace std;
class Test {
  int x; // this is private object
  Test(int x = 0): x {x} {}
  // `friend` means the function can use private properties (such as [x] of the class)
  friend istream& operator >> (istream& input, Test& obj);
  friend istream& operator << (ostream& input, Test& obj);

// Detailed Explanation: [Youtube](https://www.youtube.com/watch?v=2972LRdyquk)
istream& operator >> (istream& input, Test& obj) {
  input >> obj.x;
  return input; // return type is `istream&`

ostream& operator << (ostream& output, Test& obj) {
  output << obj.x << endl;
  return output;

int main() {
  Test t;
  cin >> t;
  cout << t;
  return 0; // success

But honestly, in above example using namespace std is a bad practice. Here is why:

Usually if we need to do string name = "something" then we actually need std::string name = "something". This is because internally, std is following:

namespace std {
  istream& cin();
  ostring& cout();

However, since people get lazy and don't want to type std::string, they using namespace std and then type string name = "something".

A better practice is to do the following:

#include <iostream>
using std::cout;
using std::endl;
using std::string;



public function with the same function name as class name is the constructor.

You can have "default constructor", "copy constructor", "move constructor"

class A {
    A(){} // default constructor
    A(const A&){} // copy constructor
    A(const A&&){} // move constructor

Note in above example, we can ignore the object name and only write type in declearation.

int main(int argc, const char* argv[]) {
  A a; // call default constructor
  A b = a; // call copy constructor
  A c = std::move(a) // call move constructor
  A d(a); // call copy constructor
  A e(std::move(a)); // call move constructor

Explicit vs. Implicit

Sometimes variables can be constructed in implicit way:

std::unique_ptr<Entity> entity = new Entity();

instead of explicit way:

std::unique_ptr<Entity> entity(new Entity())

Note that if Entity() throw exception, there might be potential memory leak. you should do std::unique_ptr<Entity> entity = std::make_unique<Entity>(); to be safe.

You can force non-implicit by doing something like this:

explicit unique_ptr(pointer _Ptr) {

Copy Constructor

A copy constructor is a member function that initializes an object using another object of the same class. A copy constructor has the following general function prototype:

ClassName (const ClassName &old_obj);

Copy constructor is used to initialize the members of a newly created object by copying the members of an already existing object. Not ethat the copy constructor is a shallow copy. Deep copy is possible only with a user-defined copy constructor.

MyClass t1, t2;
MyClass t3 = t1;  // ----> (1)
t2 = t1;          // -----> (2)

Note that (1) calls the copy constructor and (2) calls the assignment operator

Automatic Constructor Generation

In C++11, you might see default or delete keyword in constructor declearation.

class C {
  C(const C&) = default;
  C(C&&) = default;
  C& operator=(const C&) & = default;
  C& operator=(C&&) & = default;
  virtual ~C() { }

= default make use of compiler-generated version of that function, so you don't need to specify a body. Compiler can generate constructors, destructors and assignment operators.

Also notice that a default constructor would not be generated if you provide any other non-default constructor. If you still want the default constructor, too, you can use this syntax to have the compiler make one.

Use = delete to specify that you don't want the compiler to generate that function automatically.

Move constructor isn't always generated by default (e.g. if you have a custom destructor)


public function with the same function name as class name but with a ~ in front is the deconstructor.

Initialize Constants

If you have written code like following

#include <iostream>

using namespace std;
class T1 {
  const int t = 100; // Note t is not static, therefore the instant of T1 has not been created yet. You can't assign value of a const int that does not exist.
    T1() {
      cout << "T1 constructor: " << t << endl;

The compiler will tell you:

test.cpp:21: error: ISO C++ forbids initialization of member ‘t’
test.cpp:21: error: making ‘t’ static

This is because const variable is not modifiable. Instead, you need to initialize const int t in the constructor:

T1(int val) : t(val) {
  // Other constructor stuff here

The syntax t(val) is executed as a "initialization list". For example, the above code is nearly the same as the following code with minor difference.

T1(int val) {
  t = val;
  // Other constructor stuff here

By the C++ specification, the above code illegal. We cannot change the value of a const variable in the constructor, because it is marked as const. So you have to use the initialization list.


Uniform Initialization

Uniform initialization is a feature in C++ 11 that allows the usage of a consistent syntax to initialize variables and objects ranging from primitive type to aggregates. In other words, it introduces brace-initialization that uses braces {} to enclose initializer values. The syntax is as follows: type var_name{arg1, arg2, ....arg n}

The advantage is that the initialization works and looks the same for all object including array, built-in, and objects. Using bracket {} instead of () allows the compiler to distinguish initialization and function declearations more easily.

Following are some of the examples of the different ways of initializing different types:

int i; // uninitialized built-in type
int j = 10; // initialized built-in type
int k(10); // initialized built-in type
int a[] = {1, 2, 3, 4} // Aggregate initialization
X x1; // default constructor
X x2(1); // Parameterized constructor
X x3 = 3; // Parameterized constructor with single argument
X x4 = x3; // copy-constructor

If initialized using brace initialization, the above code can be re-written as:

int i{};  // initialized built-in type, equals to int i{0}
int j{10}; // initialized built-in type
int a[]{1, 2, 3, 4} // Aggregate initialization
X x1{}; // default constructor
X x2{1}; // Parameterized constructor
X x4{x3}; // copy-constructor

In most cases {} and () are the same for initialization. Minor differences involving the use of std::initializer_list<>

Initialization List

When using inheritance, you can use initialization list to initialize your parent:

#include <iostream>
class Foo {
  Foo( int x ) {
    std::cout << "Foo's constructor "
              << "called with "
              << x
              << std::endl;

class Bar : public Foo {
  Bar() : Foo( 10 ) { // construct the Foo part of Bar
    std::cout << "Bar's constructor" << std::endl;

int main() {
  Bar stool;

You can also use initialization list to initialize fields in order:

class Baz {
    Baz() : _foo( "initialize foo first" ), _bar( "then bar" ) { }
    std::string _foo;
    std::string _bar;

You can also pass input to initialization list as follow:

class Baz {
    Baz(int i) : i(i) { }
    int i;

Note that it work for primitive type as well as user-constructed type. They have the same syntax thanks for uniform initialization. i(i) above will call the copy constructor. The int type can also be a template type as long as the template type has a copy constructor:

template <class T>
class my_template {
    // works as long as T has a copy constructor
    my_template( T bar ) : _bar( bar ) { }
    T _bar;

Using initialization lists to initialize fields is not always necessary (although it is probably more convenient than other approaches). But it is necessary for const fields. If you have a const field, then it can be initialized only once, so it must be initialized in the initialization list.

Since constructor can generate error, calling initialization list can generate error. You can handel error in the following manner:

class Foo {
  Foo() try : _str( "text of string" ) {
  } catch ( ... ) {
    std::cerr << "Couldn't create _str";
    // now, the exception is re-thrown as if we'd written
    // "throw;" here

Reference and Pointer

References are often confused with pointers but three major differences between references and pointers are:

View this website on How to use Reference


#include <iostream>
int main () {
   int i;
   int& r = i;
   i = 5;
   std::cout << "Value of i : " << i << std::endl;
   std::cout << "Value of i reference : " << r  << std::endl;
   return 0;

will print the following:

Value of i : 5
Value of i reference : 5

Therefore, it is very easy to request some modification by passing reference to functions

void swap(int& i, int& j) {
  int tmp = i;
  i = j;
  j = tmp;
int main() {
  int x, y;

Note that pointer can be written in a new way:

// old way of doing C (still valid)
int x = 16;
int* y = &x;
cout << *y;

// new way of doing C++
int x = 16;
int& y = x;
cout << y;

Unique Pointer

Unique Pointer: typical standard pointers you learned in schools. Copying them is not a good idea since, after you make a copy, if the original pointed memory is freed, then copied pointer's memory is also freed, making the copied pointer's reference dangerous to access.

std::unique_ptr<Entity> entity = std::make_unique<Entity>();

Shared Pointer and Weak Pointer

This feature kinda replaced new and delete keyword. Weak Pointer must be used with Shared Pointer

std::shared_ptr<Entity> entity = std::make_shared<Entity>();

For example:

  std::shared_ptr<Entity> e;
    std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>();
    e = sharedEntity;
  // Entity out of scope for se, memory remain
// Entity out of scope for e, memory released

Well, what if we want a secure pointer but we just want to store the pointer but we don't want to keep it alive when we store it. We can use Weak Pointer. Unlike Shared Pointer, it doesn't increase reference count. (You can ask a weak pointer whether they are expired.)

std::weak_ptr<Entity> weakEntity = sharedEntity;

When using Weak Pointer, it is a good practice to check whether the pointer is valid and if so, put a lock to it to ensure no concurrent threads can free the memory region.

if (std::shared_ptr< Transform > parent_ = parent.lock()) {
  // we have "parent" memory region
} else {
  // no memory

Automatic Typing

auto keyword automatically infer return type.

Function Templating

template <typename YourTypeName1, typename YourTypeName2>
YourTypeName1 functionName(YourTypeName1 parameter1, YourTypeName2 parameter2, ...) {
    // code

template keyword means the following function can be used with arbitrary input type.

For example, we can do the following code:

#include <iostream>
using namespace std;

template <typename T>
T add(T num1, T num2) {
    return (num1 + num2);

int main() {
  int result1 = add<int>(2, 3);
  cout << "2 + 3 = " << result1 << endl;
  double result2 = add<double>(2.2, 3.3);
  cout << "2.2 + 3.3 = " << result2 << endl;
  return 0;

You can also use a template in a class to make a generic stack.

template <class T>
class Stack {
  Stack(int = 10);
  ~Stack() { delete [] stackPtr; }
  int push(const T&);
  int pop(T&);
  int isEmpty()const { return top == -1; }
  int isFull() const { return top == size - 1; }
  int size;
  int top;
  T* stackPtr;

Note that the keyword class and typename in template are equivalent. But there are differences outside the usage as described in stackoverflow

Static Variables

Static variable can be either inside a class or inside a function. A static variable is a variable that is declared using the keyword static. The space for the static variable is allocated only one time and this is used for the entirety of the program.

Once this variable is declared, it exists till the program executes. So, the lifetime of a static variable is the lifetime of the program.

// from tiny-cuda-nn
inline std::atomic<size_t>& total_n_bytes_allocated() {
  static std::atomic<size_t> s_total_n_bytes_allocated{0};
  return s_total_n_bytes_allocated;

Multiple Inheritance

class 1 {

class 2: public 1 {

In the above syntax, class 1 is the parent of class 2. The constructor of class 1 is normally executed before the constructor of class 2 when class 2 is created.

Function Declearation

In C++11, there are two syntaxes for function declaration:

return-type identifier ( argument-declarations... )


auto identifier ( argument-declarations... ) -> return_type

They are equivalent. Now when they are equivalent, why do you ever want to use the latter? Well, C++11 introduced this cool decltype thing that lets you describe type of an expression. So you might want to derive the return type from the argument types. So you try:

template <typename T1, typename T2>
decltype(a + b) compose(T1 a, T2 b);

however the compiler will tell you that it does not know what a and b are in the decltype argument. That is because they are only declared by the argument list.

You could easily work around the problem by using declval and the template parameters that are already declared. Like:

template <typename T1, typename T2>
decltype(std::declval<T1>() + std::declval<T2>())
compose(T1 a, T2 b);

except the above expression is getting very long and hard to read. So the alternate declaration syntax was proposed and implemented and now you can write

template <typename T1, typename T2>
auto compose(T1 a, T2 b) -> decltype(a + b);

C++14 also permits just

auto identifier ( argument-declarations... )

as long as the function is fully defined before use and all return statements deduce to the same type. The -> syntax remains useful for public functions (declared in the header) if you want to hide the body in the source file. Somewhat obviously that can't be done with templates, but there are some concrete types (usually derived via template metaprogramming) that are hard to write otherwise.

The above is almost-exact copy from stackoverflow

Scope Guard

#include <functional>       // std::function
#include <utility>          // std::move

namespace my {
    using std::function;
    using std::move;

    class Non_copyable
        auto operator=( Non_copyable const& ) -> Non_copyable& = delete;
        Non_copyable( Non_copyable const& ) = delete;
        auto operator=( Non_copyable&& ) -> Non_copyable& = default;
        Non_copyable() = default;
        Non_copyable( Non_copyable&& ) = default;

    class Scope_guard
        : public Non_copyable
        function<void()>    cleanup_;

        void dismiss( Scope_guard& g ) { g.cleanup_ = []{}; }

        ~Scope_guard() { cleanup_(); }

        template< class Func >
        Scope_guard( Func const& cleanup )
            : cleanup_( cleanup )

        Scope_guard( Scope_guard&& other )
            : cleanup_( move( other.cleanup_ ) )
        { dismiss( other ); }

}  // namespace my

#include <iostream>
void foo() {}
auto main() -> int
    using namespace std;
    my::Scope_guard const final_action = []{ wclog << "Finished! (Exit from main.)\n"; };

    wcout << "The answer is probably " << 6*7 << ".\n";

See stackoverflow

Atomic Operations

Atomic variables are primarily used to synchronize shared memory accesses between threads. But in C++, we have std::atomic<> atomic objects. Each atomic class has a load() and a store() operation which is utilized to perform assignments. This helps make it clearer when atomic operations are being performed rather than a normal assignment.

atomic_var1.store (atomic_var2.load()); // atomic variables
var1 = var2;   // regular variables

Each load() and store() function can take in a std::memory_order that specify how the CPU may schedule the load and store accross threads.

typedef enum memory_order {
} memory_order;

Using atomic class, the default behavior of some arithmetic operation is guaranteed to be atomic:

std::atomic<long> value(0);
value++; //This is an atomic op
value += 5; //And so is this

In most Instruction Set Architecture (ISA), simple addition + and subtraction - is itself atomic, but not +=. But with std::atomic, both are atomic.

You can overload arithmetic operations so that your overloaded function is atomic. Because operator syntax does not allow you to specify the memory order, these operations will be performed with std::memory_order_seq_cst by default.

Here is a good article with example to explain each memory order. In short, here is my interpretation (might not be correct): - relaxed: only guarantee operation itself is atomic, execution order within one thread is not guaranteed - sequentially consistent: memory and atomic operations are executed in thread order, one by one. - release/acquire: A release operation prevents ordinary loads and stores from being reordered after the atomic operation, whereas an acquire operation prevents ordinary loads and stores from being reordered before the atomic operation. - consume: same operation as acquire, only with the exception that the ordering guarantees only apply to dependent data. (apparently no major compiler implements it)

Standard Library (STD)


There are some methods:


The first thing to note is that std::move() doesn't actually move anything. It changes an expression from being an lvalue (such as a named variable) to being an xvalue. An xvalue tells the compiler: "You can plunder me, move anything I'm holding and use it elsewhere (since I'm going to be destroyed soon anyway)"

In other words, when you use std::move(x), you're allowing the compiler to cannibalize x. Thus if x has, say, its own buffer in memory - after std::move()-ing the compiler can have another object own it instead.

A typical use is 'moving' resources from one object to another instead of copying. @Guillaume links to this page which has a straightforward short example: swapping two objects with less copying.

Usually we can write:

template <class T>
swap(T& a, T& b) {
    T tmp(a);   // we now have two copies of a
    a = b;      // we now have two copies of b (+ discarded a copy of a)
    b = tmp;    // we now have two copies of tmp (+ discarded a copy of b)

But with std::move() we can:

template <class T>
swap(T& a, T& b) {
    T tmp(std::move(a));
    a = std::move(b);
    b = std::move(tmp);

Think of what happens when T is, say, vector<int> of size n. In the first version you read and write 3n elements, in the second version you basically read and write just the 3 pointers to the vectors' buffers, plus the 3 buffers' sizes. Of course, class T needs to know how to do the moving; your class should have a "move-assignment operator" and a "move-constructor" for class T for this to work.

Above is copied from stackoverflow


A tuple pair where you are allowed to pack to items together and access the first or second.


You can think of std::initializer_list as a bracket initialized array that can be used to pass in as arguments. It has no definite type definition. Note that std::initializer_list is a library and it differs from C's built-in initialization list.

#include <iostream>
#include <vector>
#include <initializer_list>

template <class T>
struct S {
  std::vector<T> v;
  S(std::initializer_list<T> l) : v(l) {
    std::cout << "constructed with a " << l.size() << "-element list\n";
  void append(std::initializer_list<T> l) {
    v.insert(v.end(), l.begin(), l.end());

template <typename T>
void templated_fn(T) {}

int main() {
  S<int> s = {1, 2, 3, 4, 5}; // copy list-initialization
  s.append({6, 7, 8}); // list-initialization in function call

  std::cout << "The vector size is now 8 ints:\n";

  for (auto n : s.v)
    std::cout << n << ' ';
  std::cout << '\n';

  std::cout << "Range-for over brace-init-list: \n";

  for (int x : {-1, -2, -3}) // the rule for auto makes this ranged-for work
    std::cout << x << ' ';
  std::cout << '\n';

  auto al = {10, 11, 12};   // special rule for auto

  std::cout << "The list bound to auto has size() = " << al.size() << '\n';

  // templated_fn({1, 2, 3}); // compiler error! "{1, 2, 3}" is not an expression. It has no type, and so T cannot be deduced
  templated_fn<std::initializer_list<int>>({1, 2, 3}); // OK
  templated_fn<std::vector<int>>({1, 2, 3}); // also OK

The above, according to wiki, will output:

constructed with a 5-element list
The vector size is now 8 ints:
1 2 3 4 5 6 7 8
Range-for over brace-init-list:
-1 -2 -3
The list bound to auto has size() = 3

Variadic Arguments

This is basically make a function that can take arbitrary many of arguments. This is achieved using code generation with templates.

// base case
template<typename T>
T Sum(T arg) {
  return arg;

// recursive case
template<typename T, typename... Args>
T Sum(T start, Args... args) {
  return start + Sum(args...);

int main() {
  // not recommend sending different types, might mess up result
  std::cout << Sum(1.0f, 1u, 3, 4, 7) << std::endl;

The const Keyword

const Class Method

class Bar {
  bool foo() const {
    data = 0; // this line is not allowed
  int data;

This means we cannot change the object's field.

const Type

The const keyword can be added anywhere in const type name. A function cannot change the value of a constant type. A function cannot change the value of a reference if it is constant.


There are different types of cast with different levels of dangerous: static_cast<>(), reinterpret_cast<>(), const_cast<>(), and dynamic_cast<>()

Compiling Errors and Misc

Inline function: inline keyword is used to tell compiler

Template function: template keyword is to virtualize types

Typename keyword: typename is for to aid compiler's parse


Some conventions:

Table of Content