Printing is expensive from a physics perspective
Solution (note: similar to N+1 problem)
std::endl
flushes bufferstdout is buffered by default, stderr is unbuffered
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
Transforms code before it reaches the compiler
#include "lib"
inserts the contents of the file lib
#define VAR VALUE
defines a symbol
g++ -DVAR=VALUE program.cc
Include Guard: Solve the double include problem
#ifndef FILE_H
#define FILE_H
... header file contents
#endif
Method called when object is first initialized
explicit
keyword in front of a ctorMethod called when object is destroyed
struct Node {
int data;
Node *next;
~Node () { delete next; }
}
The following happens when an object is destructed:
Node &operator= (Node other) {
std::swap(this->data, other.data);
std::swap(this->next, other.next);
return *this;
}
other == lvalue
? copy ctor
: move ctor
Accessing internal data in an ADT without using public interface - invalidates invariants
private
and public
access modifiersstruct
and class
: struct default access is public, class is privateGives something access to my information - one-way. Useful for iterators in an ADT
friend class X
, friend X::foo(int)
Indicates something in program fucked up
noexcept (false)
in function signature to prevent thisAllows 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;
}
init
is stored in contiguous memory; init.begin()
returns a pointerinit
) to build another (contents
); 2 copies in memoryDifference between new
and malloc
: malloc
returns NULL
, new
throws.
Regular new = operator new + placement new
Allocation: operator new(size_t n)
(strong guarantee)
n
bytes of uninitialized memoryoperator delete(thing)
Initialization (Placement New): new (address) type
type
object at address addr
addr
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++;
}
}
Args&&
is a universal/forwarding reference (can point to either an rvalue or lvalue)
&&
indicates a universal ref iff the type is ambiguous.emplace_back
, Args
is of an unknown type so Args&&
is a universal reference. We already know T
, so T&&
is an rvalue.Type&&
, cannot be const Type&&
or somethingstd::forward<Args>(args)...
preserves the rvalue/lvalue-ness of each argument
std::move
if argument is an rvalue, else does nothingA function f
can offer 3 different levels of safety once an exception has been handled:
f
either succeeds completely, or not at all.noexcept
std::move_if_noexcept
: std::move(x)
if move ctor is noexcept
, else x
Inheritance allows you to do something like Book b = Manga { ... }
.
bool hasAnime
- are "sliced"b.isCool
would call Book::isCool
rather than Manga::isCool
auto bp = make_unique<Manga>(...)
// 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
isCool
Every object (instance) has a vptr, a pointer to that table
book.isCool
or manga.isCool
are called, it performs the lookup using the vptr and runs the corresponding functionFor "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.
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.
T
is a reference, throws bad_cast
if cast failsHow do dynamic casts work?
dynamic_cast
will not work on classes without virtual methods (i.e. non-polymorphic) and will throw instead