- compatible with C (or at least used to be)
- basic syntax is quite similar to Java
std::endlvs\n-std::endlflushes the buffer,\ndoesn't- flushing the buffer can be expensive, so use
\nunless you need to flush the buffer (log statements, ...)
- flushing the buffer can be expensive, so use
gcc- GNU Compiler Collectiong++- GNU C++ Compiler (C++ frontend forgcc, under the hood callsgccwith some flags)- standard library is installed together with the compiler, located in
/usr/include/c++/VERSIONVERSIONis the version of the compiler, not C++ standard- unlike Java, compilers implement the new standards incrementally - see C++ compiler support
- static libraries -
.afiles- part of the executable
- dynamic libraries -
.sofiles (Linux),.dllfiles (Windows)- loaded at runtime
- integers
signed,unsignedshort- ~ 2 bytes (depends on the compiler/platform)long- ~ 4 byteslong long~ 8 bytesint64,uint64, ..typedef signed long int __int64_t; typedef unsigned long int __uint64_t;
- floating numbers
- float, double, long double
- characters
signed,unsignedchar- 1 bytewchar_t- 2 bytes (unicode) - uses prefixL- and more
- boolean
true= 1false= 0
std::Byte- 8 bitssize-t
auto~varnullptr~nullinclude~importinclude <something>- search in system directories (STL)include "something"- search in the current directory
using NAMESPACE~static importconst var~final vartemplates~generics, templates can do much more (compile-time code generation)static~private static- static objects/functions are not exported outside the CPP fileextern~public staticthread_localmodifier ~ThreadLocalclass- boolean expressions -
0isfalse, anything else istruenullptris0, i.e.false
- C++
charis just a 1-byte integer- no
bytetype, usechar - use
wchar_tfor Unicode
- no
- arrays don't know their size, either use
std::arrayor hold the size separately []no range checks (containers, arrays) - goes into invalid memory
- Use error codes - failure is normal and expected (like reading a file), immediate caller
- throw exception - caller is not immediate, undo action is possible
- Terminate - irrecoverable errors
noexcept- function will not throw exceptionstry { .. } catch (std::exception& e) { .. }try { .. } catch (...) { .. }- expression...catches all exceptions
- to avoid name collisions and organize code
::- scope resolution operator- global namespace - no prefix, we can even use e.g.
::MyObject using namespace std- imports everything from a given namespace, not really recommended because it pollutes the global namespaceusing std::cout, std::string- selective imports - recommendedusing foo = unsigned int- aliases (types, function pointers, ...)using size_t = unsigned int;- implementation specific, some compilers useunsigned long- dynamic!- template param partial binding -
template <typename K> using Map<K> = std::Map<K, Foo>;
usingcan be used on the global (namespace) level, in a function, or in a block
- initialization
- standard:
int a = 5; - using double brackets:
int a {5},int a[] {1, 2, 3}
- standard:
constexpr- compile-time constants and pure functions, stored in read-only memoryconstexpr int dmv = 17;constexpr double square(double x) { return x*x; }- pure functions
- test auto conversions
if (x)-xis converted tobool0to falsenullptrto false
std::stringis a class, not a primitive type- is mutable
string s = "aaa"; s[0] = 'b';works- you can use
constto make it immutable
- you can use
<cctype>-isalnum, isalpha, is..util functions<<read by word,getline(&s)read by line- C-style string is an array of characters terminated by a null character
- best avoided
"foo"literal isconst char[N], to make it a string use suffixs
- prefer brace initialization (
int{5}), it prevents most of the undefined and weird behavior - any pointer can be implicitly converted to
bool-nullptrisfalse, anything else istruevoid*pointer
- any number can be implicitly converted to
bool-0isfalse, anything else istrue
- always type safe and non-narrowing
(desired-type)object-to-cast- mostly for compatibility with C, better avoid
[explicit] operator destination-type() const { }- if explicit, compiler will not perform implicit conversions, a static cast is required
Example
operator bool() const { }
-
explicit type conversions
-
static_cast - compile-time cast
static_cast <new_type> (expression);- used to convert between related types (e.g. int to double or inherited classes)
-
dynamic_cast - runtime cast - polymorphism
dynamic_cast <new_type> (expression);- mainly used to perform downcasting (converting a pointer/reference of a base class to a derived class)
- dynamic cast to a reference type - if the cast fails, an exception of type
std::bad_castis thrown - dynamic cast to a pointer type - if the cast fails, the result is a null pointer of the target type
-
const_cast - cast away constness
const_cast <new_type> (expression);- used to add or remove
constorvolatilequalifiers
-
reinterpret_cast - low-level cast
reinterpret_cast <new_type> (expression);- used to convert the pointer to any other type of pointer
- no type checking is performed
!!erris the same as!(!err)which meanserr != nullptrerris a pointer- see https://stackoverflow.com/questions/11374810/defining-double-exclamation
- dynamic memory (heap, free store) - allocated at runtime
- option 1 -
new&delete- "C++" style (preferred)newreturns a pointer to the allocated memoryint* myInt = new int(123);
deletefrees the memorydelete myInt;
- option 2 -
malloc&free- "C" style (avoid if possible)mallocreturns a pointer to the allocated memoryint* myInt = (int*) malloc(sizeof(int));
freefrees the memoryfree(myInt);
- option 1 -
- better to use smart pointers (
std::unique_ptr,std::shared_ptr) instead of raw pointers
- An object must be constructed (initialized) before it is used and will be destroyed at the end of its scope.
- For a namespace object the point of destruction is the end of the program.
- For a member, the point of destruction is determined by the point of destruction of the object of which it is a member.
- An object created by new “lives” until destroyed by
delete(ordelete[]for arrays).deletefrees the memory, doesn't "destroy" the pointer
- Once the object is deleted, it's a good practice to set the pointer to
nullptrto avoid dangling pointers - It's important to delete the pointer (free the memory) before the pointer goes out of scope or is reassigned
- by default, variables are allocated on the stack, heap is used for dynamic memory allocation (new, malloc)
- to prevent multiple inclusion of the same (header) file (alternative is
#pragma once)- prevents double declaration of any identifiers such as types, enums and static variables
- prevents infinite recursion
#ifndef LEARNING_CPP_HOUSE_H
#define LEARNING_CPP_HOUSE_H
...
#endif //LEARNING_CPP_HOUSE_H
- templates provide a general mechanism for compile-time programming (code generation)
- templates are compile-time while virtual methods are runtime
- classes, functions, constructors, variables, ...
template<typename T>
class Vector {
private:
T* elem; // elem points to an array of sz elements of type T
int sz;
// variable template
template<typename T>
constexpr T pi = T(3.1415926535897932385);- value template arguments
template<typename T, int N>
struct Buffer {
constexpr int size() { return N; }
T elem[N]; operator[]subscript operatoroperator=assignment operatoroperator()application operator, also called “function call” or just “call”operator""- user-defined literalsvoid * operator new(size_t size)- custom memory allocationvoid operator delete(void * p)- custom memory deallocation
void* operator new(size_t size, void * p);void operator delete(size_t size, void * p);- allows to construct an object in a pre-allocated memory
- constructed object must not be deleted with
delete, a destructor must be called explicitly new (address) Type;- see https://www.geeksforgeeks.org/placement-new-operator-cpp/
[&](int a){ return a<x; }[]- capture list&- capture by reference=- capture by value (copy)this- capture thethispointera, b- capture justaandbby value
mutable- allows the lambda to modify the captured variables
[[deprecated]][[nodiscard]]- compiler will raise a warning if the function return value is not used- applies to pure functions, i.e. when the function has no side effects and calling it without using the returned value makes no sense
[[likely]]and[[unlikely]]- hint to the compiler about the expected branch
- similar to
cout/cin, uses<<and>>operators
#include <fstream>
// file write
ofstream myfile;
myfile.open("example.txt");
myfile << "Writing this to a file.\n";
myfile.close();
// file read
ifstream myfile;
myfile.open("example.txt");
string line;
while (!myfile.eof()) {
getline(myfile, line);
cout << line << endl;
}
- compile just
cppfiles, nothfiles (they are included in thecppfiles) g++ -o main main.cpp util.cpp- compile and link- 3 phases
- files are compiled separately (aka translation units), linker links them together
- preprocess - process includes files (headers) and macros - one text file (translation unit)
- compile - compile the preprocessed files (one by one, no dependencies, can be done in parallel) into object files (
foo.o) - link - link object files and static libraries into an executable
[]no range checks- doesn't know its size, we always need to pass it
- values are not initialized by default!
- we can provide fewer values than then total size, remaining values will be 0
int counters[10] = {0}- all zeroesint counters[10] = {1}- 1 and nine 0s
- we can provide fewer values than then total size, remaining values will be 0
- just a pointer to the first element
int arr[10]can be assigned asint* ptr = arr
- pointer arithmetic
*ptr = 1is the same asarr[0] = 1*(arr + 3)is the same asarr[3]
- static
- "double-duty"
- internal linkage - function/variable is only visible in the current file (translation unit)
- lifetime is the entire program ("static")
- "double-duty"
- extern
- external linkage - for global functions/variables, I'm saying it's defined somewhere else
- implies lifetime is the entire program ("static")
- inline - this function will be defined in multiple translation units, don't worry about it
- stack frames are added on top of each other (new stack frame has lower memory address than the previous one)
- frame consists of in the following order (ascending memory):
- parameters
- local variables
- saved registers
rbpandrip
- passing arguments between stack frames
- by reference - pointer to the function argument is passed
- by value
- small objects (like ints) are copied directly into child's stack frame
- large objects (like structs) are first copied on parent's stack as a new local variable and then a pointer to the copy is passed
- Resource Acquisition Is Initialization
- pairing allocation with de-allocation - prevents memory leaks
struct Box {
Box() { buffer = new char[1024]; }
~Box() { delete[] buffer; }
private:
char * buffer;
};