Buffering

Printing is expensive from a physics perspective

Solution (note: similar to N+1 problem)

stdout is buffered by default, stderr is unbuffered

Makefile

CXX = g++
CXXFLAGS = -std=c++14 -Wall -g -MMD
EXEC = my_program
OBJECTS = main.o dependency.o
DEPENDS = ${OBJECTS:.o=.d}

${EXEC}: ${OBJECTS}
	${CXX} -o ${OBJECTS} ${EXEC}

-include: ${DEPENDS}

.PHONY: not_a_filename

not_a_filename:
	some_command

-MMD flag to generate dependencies

Preprocessor

Transforms code before it reaches the compiler

#include "lib" inserts the contents of the file lib

#define VAR VALUE defines a symbol

Include Guard: Solve the double include problem

#ifndef FILE_H
#define FILE_H
... header file contents
#endif

Constructors

Method called when object is first initialized

Destructors

Method called when object is destroyed

struct Node {
	int data;
	Node *next;
	~Node () { delete next; }
}

The following happens when an object is destructed:

  1. Destructor is called
  2. Dtors of object fields called in reverse declaration order
  3. Superclass is destructed
  4. Space deallocated

Unified Assignment Operator

Node &operator= (Node other) {
	std::swap(this->data, other.data);
	std::swap(this->next, other.next);
	return *this;
}

Tampering

Accessing internal data in an ADT without using public interface - invalidates invariants

Friend

Gives something access to my information - one-way. Useful for iterators in an ADT

Exceptions

Indicates something in program fucked up

Initializer List

Allows for array-style initialization vector<int> v { 1, 2, 3 };

#include <initializer_list>
vector (std::initializer_list<T> init) :
	n { init.size() }, cap { init.size() }, contents { new T[cap] } {
	size_t i = 0;
	for (auto &t: init) contents[i++] = t;
}

Operator and Placement New

Difference between new and malloc: malloc returns NULL, new throws.

Regular new = operator new + placement new

Allocation: operator new(size_t n) (strong guarantee)

Initialization (Placement New): new (address) type

Variadic Arguments and Perfect Forwarding

Same syntax and functionality as spread

template <typename T> class Vector {
	template <typename ...Args> 
	void emplace_back (Args&& ...args, T&& example) {
		new (contents + n) T(args...);
		n++;
	}
}

Exception Safety

A function f can offer 3 different levels of safety once an exception has been handled:

Slicing

Inheritance allows you to do something like Book b = Manga { ... }.

Virtual Methods and Override

// assume same definitions as above
class Book {
	...
	public:
		virtual bool isCool () const { return true; }
}

class Manga : public Book {
	...
	public:
		bool isCool () const override { return hasAnime; }
}

Every class has a "virtual table", or vtable, which contains all of its virtual methods

Every object (instance) has a vptr, a pointer to that table

Casting

For "transforming" types. dynamic_cast<T>(val) is the only one with a runtime effect; all others are compile time casts with O(0) runtime.

(T) val: C-style casting. Combination of the three below. Only use for numbers.

static_cast: Compile time type assurance. You know the type beforehand.

reinterpret_cast: God knows what this does. Don't use it. Intentional UB.

const_cast: Adds or removes const qualifier. Just make your program const-correct and you won't have to use this.

dynamic_cast: See below. Too many details.

Dynamic Casting

Returns val with type T, allowing you to use T's methods, or nullptr if the cast is invalid. For if you don't know the exact type at runtime.

How do dynamic casts work?