Notes

C++

old name is C with Classes.

Contains 4 parts, each designed with different philosophy.

  • C

  • Object-oriented programming

  • Template (generic programming)

  • STL (standard library)

Tutorial

C Library

#include <cstdio>
#include <cstdlib>
#include <cstring> // memset
#include <cmath>
c++

C++ Library

#include <iostream>

#include <iostream>
 
using namespace std;
 
int main() {
    char name[50];

    cin >> name;
    cout << "Name: " << name << endl;
 
}
c++

#include <string>

#include <iostream>
#include <string>
 
using namespace std;
 
int main ()
{
    string str1 = "Hello";
    string str2 = "World";
    string str3;
 
    str3 = str1 + str2;
    s = s + 'c';
   
    int len = str3.size(); // or str3.length()
    
	int idx = str3.find("pattern"); // -1 (string::npos) if not found
    
    char* cstr3 = str3.c_str();
    
    // erase
    s.erase(s.begin() + i); // erase one char
    s.erase(start, num); // erase from start for num chars
    
    // string to int
    // int stoi(string& s, size_t* idx=0, int base=10)
    string s = "123";
    int i = stoi(s); // 123
    
    string s = "123 xyz";
    size_t sz;
    int i = stoi(s, &sz); // 123, sz = 3
    
    string s = "-10010110001";
    int b = stoi(s, nullptr, 2); // -1201, binary.
    
    // string to float
    string s = "12.34";
    float f = stof(s); // 12.34
    
    // similarly, we have `stol, stoll, stod, stold, stoul, stoull`
        
    // int/float/double to string
    string s = to_string(42); // "42"
    string s = to_string(12.23); // "12.23"
    string s = to_string(1e40); // "10000000000000000303786028427003666890752.000000"
}
c++

#include <vector>

#include <iostream>
#include <vector>
using namespace std;
 
int main() {
    // init
    vector<int> vec; 
    vector<int> vec = {1, 2, 3};
    vector<int> vec(size[, init_value]);
    
    // 2d init: [N, M], -inf
    vector<vector<int>> dp(N, vector<int>(M, -0x3f3f3f3f));
    
    // reserve v.s. resize
    vec.reserve(10); // only increase capacity, will not push empty elements! vec.size() == 0
    vec.resize(10); // push 10 empty elements into vec. vec.size() == 10
    
 	// push_back
    for(int i = 0; i < 5; i++){
       vec.push_back(i);
    }
    
    // clear
    v.clear();
        
    // size
    cout << vec.size() << endl;
 	
    // access by index
    for(i = 0; i < 5; i++){
       cout << i << " = " << vec[i] << endl;
    }
 	
    // iterator
    vector<int>::iterator v = vec.begin();
    while(v != vec.end()) {
       cout << *v << endl; // same as a pointer
       v++;
    }
    
    // loop and reversed loop
    for (auto it = v.begin(); it != v.end(); it++) {...}
    for (auto it = v.rbegin(); it != v.rend(); it++) {...} // also use ++!
    
    // erase i-th element
    v.erase(v.begin() + i);
    
    // copy another vector
    vector<int> v2(v1);
    
    // insert x at i-th position
    v.insert(v.begin() + i, x);
    
    // extend another vector (also by insert)
    v1.insert(v1.end(), v2.begin(), v2.end());
    
    // emplace_back: in-place push_back  (cpp11, but not supported in msvc10)
    // use condition: push a complicated object to a vector, but don't want to create a temporary object and then copy it to the vector.
    // assume we have a Complicated class initialized by Complicated(x, y, z)
    vec<Complicated> v;
    v.push_back(Complicated(x, y, z)); // create temporary then copy, slow.
    v.emplace_back(x, y, z); // no temporary, fast.

    // emplace: in-place insert.
    v.emplace(v.end(), x, y, z);
    
    // slice subvector
    vector<int> v2(v.begin(), v.begin() + 5); // select and copy [0, 5)
    v.resize(5); // inplace trick, keep first 5 elements
    
    // iterator
	auto it = v.begin();
    auto next = it + 1;
    auto nnnnext = it + 4; // vector is sequence container, so we can add an int to it.
    int x = *it; 
    v.erase(next);
    v.insert(next, 0);
    
 	
    return 0;
}
c++

Note: vector<bool> is not a STL container! It optimizes space and use bit to save each bool variable, so many STL operations are not supported. Use bitset or deque<bool> instead.

#include <set>

set implements a binary search tree, so querying existence and order is in O(log n)

#include <iostream>
#include <set>
using namespace std;
 
int main() {
    set<int> s;
    
    // insert element
    s.insert(1);
    
    // count element, can check if an element is in the set.
    cout << s.count(1) << endl;
    bool is_in = s.count(1);
    
    // find the first element, another way to check is in. (faster than count!)
    bool is_in2 = s.find(1) != s.end();
    auto it = s.find(1); // return iterator to the first found element.
	
    // [c++20] contains
    bool is_in3 = s.contains(1);
    
    // delete by value
    s.erase(2); // erase(value)
    
    // size
    cout << s.size() << endl;
    
    // clear
    s.clear();
    
    // is empty
    if (s.empty()) {
        //...
    }
    
    // loop all elements (increasing order)
    for (auto it = s.begin(); it != s.end(); it++) {
        cout << *it << endl;
    }
    // loop with index (enumerate)
    for (auto [it, i] = tuple{s.begin(), 0}; it != s.end(); it++, i++) m[*it] = i;
    
	// get the smallest (first) or largest (last) element
    int mn = *s.begin();
    int mx = *s.rbegin(); // 注意 *s.end() 是未定义行为
    
    // get the k-th smallest element
    auto i = s.begin();
	cout << *i << endl; // s[0]
    auto next = ++i; // s[1], DO NOT USE i++, then next is still s[0]
    
    advance(i, k); // in-place modification of i (since set is an associative container, i + k will not work)
    
    auto i = s.begin();
	auto i2 = next(i, 1); // not in-place ver
    auto i3 = prev(i2, 1);
    
    int mmn = *next(s.begin(), 1);
}
c++

#include <map>

map implements binary search tree to store keys.

#include <iostream>
#include <map>
using namespace std;
 
int main() {
    // init
    map<int, string> m;
    map<int, int> n = {{0, 1}, {1, 2}};
    // insert
    m[0] = "zero"; // m[key] = val
    m.insert(pair<int, string>(1, "one")); // m.insert(pair)
    
    // get
	cout << m[-1] << endl; // if not exist, will insert & initialize it ! (here int --> 0)
    cout << m.at(-1) << endl; // check if exist first, throw an error
    
    // size
    m.size();
    m.empty();
    m.clear();
    
    // iterator
    // auto == std::map<int,string>::iterator == pair<int,string>
    for (auto it = m.begin(); it != m.end(); it++) {
        cout << it->first << ": " << it->second << endl;
    }
    for (auto& it : m) {
        cout << it.first << ": " << it.second << endl;
    }
    for (auto& [k, v]: m) { // c++17
        cout << k << ": " << v << endl;
    }
    
    // delete
    m.erase(0); // erase(key)
    
    // find
    auto it = m.find(2);
    if (it != m.end()) {
        m.erase(it);
    }
    
    // check if a key is in map
	bool is_in = m.find(0) != m.end();
    bool is_in2 = m.count(0); // also work in map
    
    // binary search keys
    // assume the keys are {0,1,3,5,7}
    auto it = m.lower_bound(3); // will get iterator to 3
    auto it = m.lower_bound(2); // will get iterator to 3
    auto it = m.lower_bound(-1); // will get iterator to 0
    auto it = m.lower_bound(8); // will get map.end(), which has `it->first = map.size(); it->second = 0` (注意不要用key来判断是否达到了map.end()!这里key等于map中的元素数量。要用it == map.end()。)
    
    auto it = m.upper_bound(3); // will get iterator to 5
    auto it = m.upper_bound(7); // will get map.end()

    
}
c++

#include <unordered_set>

unordered_set implements a hash table, thus faster than ordered set.

API is the same with set.

#include <iostream>
#include <unordered_set>
using namespace std;
 
int main() {
    unordered_set<int> s;
    s.insert(1);
    s.erase(1);
    s.empty();
    s.clear();
    s.find(1);
    s.count(1);
    
    // regular use to check duplicate
    unordered_set<int> vis;
    while(...) {
    	if (vis.count(x)) return true;
		else vis.insert(x);
    }
    return false;
}
c++

#include <unordered_map>

unordered_map implements a hash table.

API is the same with map.

#include <queue>

#include <iostream>
#include <queue>
using namespace std;

int main() {
    queue<int> q;
    q.push(1); // not push_back
    while (!q.empty()) {
		int a = q.front(); 
        int b = q.back(); 
        q.pop(); // return void
        cout << q.size() << endl;
    }
}

// a trick in BFS to get each layer of a tree (only use one queue!)
bool BFS_leveling(TreeNode* root) {
    // bfs leveling
    queue<TreeNode*> q;
    q.push(root);
    int layer = 0;
    while (!q.empty()) {
        // record current size
        int cur_size = q.size();
        // lnodes in current layer
        for (int i = 0; i < cur_size; i++) {
            TreeNode* p = q.front(); q.pop();
            // do something...
            if (p->left) q.push(p->left);
            if (p->right) q.push(p->right);
        }
        layer++;
    }
}
c++

#include <stack>

#include <iostream>
#include <stack>
using namespace std;

int main() {
    stack<int> s;
    s.push(2); // not push_back
	int x = s.top();
    s.pop(); // return void
    s.empty();
}
c++

#include <priority_queue>

#include <iostream>
#include <queue> // priority_queue is define
using namespace std;

// < means larger-first
struct cmp {
    bool operator()(int a, int b) {
        return a < b;
    }
};

int main() {
    // heap, default is larger-first
    priority_queue<int> pq;
    pq.push(1);
    pq.push(2);
    pq.push(0);
    p.top(); // 2, yes it's top, not front.
    
    // smaller-first
    priority_queue<int, vector<int>, greater<int>> pq;
    
    
    // custom cmp function by struct
	set<int, cmp> s;
	priority_queue<int, vector<int>, cmp> pq;
    
    // custom cmp function by lambda
    auto cmp = [&](const pair<int, int>& x, const pair<int, int>& y) {
        return arr[x.first] * arr[y.second] > arr[x.second] * arr[y.first];
    };
    priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)> q(cmp);
}

// you may need to store a pair in priority queue and sort by a key:
typedef pair<int,int> pii;
priority_queue<pii, vector<pii>, greater<pii>> pq; // smaller first
// unfortunately, you cannot update the pq in-place, if you would like to modify it, you have to pop and re-push.
auto [k, v] = pq.top(); pq.pop();
pq.push(pii(k))
c++

#include <algorithm>

//// quick sort
vector<int> v = {0, 2, 1, 3};
sort(v.begin(), v.end()); // small -> large

// reverse sort
sort(v.begin(), v.end(), greater<int>());  // large -> small, c++14
// or in two steps
sort(v.begin(), v.end());
reverse(v.begin(), v.end()); // large -> small

int v[4] = {4, 2, 3, 1};
sort(v, v + 4);

// sort with custom funciton / object.
bool cmp(int i, int j) {return i < j;} // True -> [i, j]
struct cmp {
    bool operator < (int i, int j) {
        return i < j;
    }
}
sort(v, v + 4, cmp); // same as default, small -> large
sort(v, v + 4, [](int i, int j) { return i < j; }) // lambda version
    
//// merge sort O(nlogn)
stable_sort(v, v + 4);

//// partial sort / topk, in O(klogn)
partial_sort(v, v + 2, v + 4); // only sort the first two elements

//// swap
swap(x, y);

//// is_sorted
bool flag = is_sorted(v, v+4);

//// min/max_element
cout << *min_element(v, v+4) << " ~ " << *max_element(v, v+4) << endl; // return pointer/iterator

//// nth_element, O(n) quick sort
nth_element(v, v+2, v+4); // rearrange v[2] as the second element if ordered.
cout << v[2] << endl;

//// prev/next_permutation
int xs[3] = {1, 2, 3};
do {
    for (int i=0; i<3; i++) cout << xs[i] << " ";
    cout << endl;
} while (next_permutation(xs, xs+3));

//// unique
auto end = unique(v.begin(), v.end());
v.erase(end, v.end());
int new_size = v.size();

int new_size = unique(v, v+4) - v;

//// lower/upper_bound
// check out of bound conditions!
// lower_bound(begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
// upper_bound(begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
// lower/upper_bound([1,2,3], 4) --> 3
// lower/upper_bound([1,2,3], 0) --> 0

// the first position >= target
int l = lower_bound(v.begin(), v.end(), target) - v.begin();
// the first position > target
int r = upper_bound(v.begin(), v.end(), target) - v.begin();

// border condition 
if (l == v.size()) {
	// v[l-1] < target, non-exist case
} else {
    // v[l] >= target
    if (v[l] == target) {}
    else {}
}

// find the last position <= target
if (l == v.size()) return l-1;
else if (l == 0) {
    if (v[l] == target) return l;
    else return -1; // non-exist case
}
else {
    if (v[l] == target) return l;
    else return l-1;
}


//// reverse
string s = "string";
reverse(s.begin(), s.end()); // inplace

//// argsort (has to use another vector of pair)
vector<pair<int, int>> v2;
for (int i = 0; i < v.size(); i++) {
    v2.push_back({v[i], i}); // pair value with index
}
sort(v2.begin(), v2.end()); // can use default sorting of pair<int, int>, lower to higher
vector<int> idx;
for (int i = 0; i < v2.size(); i++) {
    idx.push_back(v2[i].second);
}
c++

#include <bitset>

#include <bitset>

bitset<1000> bs; // 1000 bits

bitset<32> bs{10}; // binary form of x (only support unsigned int/long long)
bitset<8> bs{0b00001111}; // just 00001111
bitset<6> bs{"010101"}; // just 010101

cout << bs << endl; // bit format

// access pos bit, NOTE: the order is reversed! e.g., bs[0] = 1 for "010101"
// out-of-bound is undefined!
bool x = bs[pos]; 

bs.all();
bs.any();
bs.none();

int cnt = bs.count(); // count number of 1
int l = bs.size();

bs.set(); // set all to 1
bs.set(pos, val=true); // set pos to 1 or 0
bs.reset(); // set all to 0
bs.reset(pos); // set pos to 0, equals bs.set(pos, false);
bs.flip(); // flip all
bs.flip(pos); // flip pos
    
// count bit function
int countBit(int x) { // assume positive!
    return bitset<32>(x).count();
}
cpp

Class

class A {
// access modifier (default is private)
private:
	int p; // private variable
public:
    // constructor
    A() {} // default
    A(int _a) { a = _a; } 
    A(int _a): a(_a) {} // init-list
    
    A(const A& other) { if (this != &other) *this = other; } // default copy constructor
    
    // operator overload
    A& operator=(const A& other) {
        a = other.a;
        return *this;
    }
    
    // destructor
    ~ A() {}
    
    // member
    int a;
    
    // method
    void setA(int _a) { a = _a; }
};

A a;
A a(0);
A a = A(0);
A* pa = new A;
A* pa = new A(0);

printf("%d", a.a);
a.setA(1);
c++

Struct is like an default-to-public Class:

struct A {
    int a; // everything is public
    void setA(int _a) { a = _a; }
};
c++

Inheritance

class B: public A {
public:
    void printA() { printf("%d", a); } // member a is inherited from class A
};
c++

operator overload

class A {
public:
    int x;
    // constructor 
    A (int x):x(x) {}
    
    // A = 1
    void operator= (int rhs) {
        x = rhs;
    }
    
    // ++A
    A& operator++ () {
        x++;
        return this;
    }
    // A++, use a fake parameter
    A operator++ (int) {
        A tmp(*this);
        x++;
        return tmp;
    }
    
    // -A
    A operator- () {
        return A(-x);
    }
    
    // A + A
    A operator+ (const A& rhs) {
        return A(x + rhs.x);
    }
    // A + 1
    A operator+ (const int rhs) {
        return A(x + rhs);
    }
    // 1 + A
    friend A operator+ (const int lhs, const A& rhs) {
        return A(lhs + rhs.x);
    }
    
    // A < 1
    bool operator< (const int rhs) {
        return x < rhs;
    }
    
    // A[i]
    int& operator[] (int i) {
        return arr[i];
    }
    
    // A(a)
    void operator() (int a) {
		return a + x;
    }
    
    // cout << A;
    friend ostream &operator << (ostream& output, const A& a) {
        output << a.x;
        return output;
    }
    
    // cin >> A;
    friend istream &operator >> (istream& input, A& a) {
        input >> a.x;
        return input;
    }
    
};
c++

polymorphism

c++

Template

Function

template <typename T>
T const& max(T const& a, T const& b) {
    return a < b ? b : a;
}
c++

Class

template <typename T>
class A {
public:
    vector<T> a;
    void push(T const& x) { a.push_back(x); }
    T top() const { return a.back(); }
}
c++

new & delete

char* p = new double;
delete p;

char* s = NULL;
s = new char[20];
delete [] s;
c++

shared_ptr

a smart pointer class, that manages reference count of the object and automatically deconstruct the object if reference count is 0.

#include <memory>

std::shared_ptr<ClassName> p(new ClassName());
std::shared_ptr<ClassName> p = std::make_shared<ClassName>();

p.reset(new ClassName()); // the first object is deconstructed.
c++

Reference

less powerful but safer than pointer. it works like an alias.

  • reference must be initialized as soon as created.

  • reference cannot be made to refer to another object once created. (cannot be re-initialized)

  • reference cannot be null. (thus there is no container of references.)

ref as an alias:

int i = 0;
int & j = i; // j is an alias of i
j = 1; // i == 1 is true

int & k; // error, not initialized !

int f() { return 42 ; };
int (& rf)() = f; // reference to a func
c++

ref as function return value:

double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};
double& setValues(int i) { return vals[i]; }
setValues(0) = 0; // okay
c++

ref as function args:

void swap(int& x, int& y) {
    int tmp = x; x = y; y = tmp;
}
c++

Memory Layout

img

  • Text Segment: the executable program itself. often read-only.
  • Initialized Data Segment (DS, data segment): initialized global variables, static variables.
  • Uninitialized Data Segment (BSS, block started by symbol): uninitialized global variables, static variables.
  • Stack: for normal memory allocation (mostly int x[size]), also contains the program stack, to enable recursive functions.
  • Heap: for dynamic memory allocation.
// C++ code -- non-vector parts are also true for C 
 
char* s = "hello world!"; // DS, read-write
const int debug = 1; // DS, read-only

int arr1[100000]; // BSS 
int N; // BSS
vector<int> arr2; // HEAP 
 
struct DumbStruct { 
    int someArr[10000]; 
}; 
 
int main () { 
    int arr3[100000]; // STACK
    static int arr7[100000]; // BSS 
    
    vector<int> arr4; // HEAP 
    
    int* arr5 = new int[100000]; // HEAP 
    int* arr6 = (int*) malloc(100000 * sizeof(int)); // HEAP 

    DumbStruct struct; // STACK 
    
    DumbStruct* struct2 = new DumbStruct(); // HEAP 
    vector<DumbStruct> structarr; // HEAP 
    
    int n; 
    scanf("%d", &n); 
    int arr8[n]; // STACK (assuming C99 -- this does not compile in C++) 
}
cpp

Type to search.