├── General ├── Graphs.png ├── Heaps.png ├── Legend.png ├── MaxHeap.png ├── Sorting.png ├── Searching.png ├── BinarySearchTree.png ├── Complexity Chart.png ├── Data Structures.png └── Data Structures Selection.png ├── Sorting ├── Heapsort.pdf ├── Quicksort.pdf ├── Bubble Sort.pdf ├── Merge Sort.pdf ├── Insertion Sort.pdf ├── Selection Sort.pdf └── Animations │ ├── Heapsort.gif │ ├── Bubble Sort.gif │ ├── Merge Sort.gif │ ├── Quicksort.gif │ ├── Merge Sort 2.gif │ ├── Insertion Sort.gif │ ├── Selection Sort 2.gif │ └── Selection Sort.gif ├── Searching ├── Binary Search.pdf ├── Breadth-First Search.pdf ├── Depth-First Search.pdf ├── Dijkstras Algorithm.pdf └── Animations │ ├── Dijkstras 1.gif │ ├── Dijkstras 2.gif │ ├── Binary Search.gif │ ├── Depth-First Search.gif │ └── Breadth-First Search.gif ├── README.md ├── NP-complete └── knapsack │ ├── test_knapsack.cpp │ ├── knapsack.hpp │ └── knapsack.cpp ├── Data Structures and Algorithms.md └── C++ Syntax.md /General/Graphs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/General/Graphs.png -------------------------------------------------------------------------------- /General/Heaps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/General/Heaps.png -------------------------------------------------------------------------------- /General/Legend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/General/Legend.png -------------------------------------------------------------------------------- /General/MaxHeap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/General/MaxHeap.png -------------------------------------------------------------------------------- /General/Sorting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/General/Sorting.png -------------------------------------------------------------------------------- /General/Searching.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/General/Searching.png -------------------------------------------------------------------------------- /Sorting/Heapsort.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Sorting/Heapsort.pdf -------------------------------------------------------------------------------- /Sorting/Quicksort.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Sorting/Quicksort.pdf -------------------------------------------------------------------------------- /Sorting/Bubble Sort.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Sorting/Bubble Sort.pdf -------------------------------------------------------------------------------- /Sorting/Merge Sort.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Sorting/Merge Sort.pdf -------------------------------------------------------------------------------- /Sorting/Insertion Sort.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Sorting/Insertion Sort.pdf -------------------------------------------------------------------------------- /Sorting/Selection Sort.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Sorting/Selection Sort.pdf -------------------------------------------------------------------------------- /General/BinarySearchTree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/General/BinarySearchTree.png -------------------------------------------------------------------------------- /General/Complexity Chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/General/Complexity Chart.png -------------------------------------------------------------------------------- /General/Data Structures.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/General/Data Structures.png -------------------------------------------------------------------------------- /Searching/Binary Search.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Searching/Binary Search.pdf -------------------------------------------------------------------------------- /Sorting/Animations/Heapsort.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Sorting/Animations/Heapsort.gif -------------------------------------------------------------------------------- /Searching/Breadth-First Search.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Searching/Breadth-First Search.pdf -------------------------------------------------------------------------------- /Searching/Depth-First Search.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Searching/Depth-First Search.pdf -------------------------------------------------------------------------------- /Searching/Dijkstras Algorithm.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Searching/Dijkstras Algorithm.pdf -------------------------------------------------------------------------------- /Sorting/Animations/Bubble Sort.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Sorting/Animations/Bubble Sort.gif -------------------------------------------------------------------------------- /Sorting/Animations/Merge Sort.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Sorting/Animations/Merge Sort.gif -------------------------------------------------------------------------------- /Sorting/Animations/Quicksort.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Sorting/Animations/Quicksort.gif -------------------------------------------------------------------------------- /Searching/Animations/Dijkstras 1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Searching/Animations/Dijkstras 1.gif -------------------------------------------------------------------------------- /Searching/Animations/Dijkstras 2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Searching/Animations/Dijkstras 2.gif -------------------------------------------------------------------------------- /Sorting/Animations/Merge Sort 2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Sorting/Animations/Merge Sort 2.gif -------------------------------------------------------------------------------- /General/Data Structures Selection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/General/Data Structures Selection.png -------------------------------------------------------------------------------- /Searching/Animations/Binary Search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Searching/Animations/Binary Search.gif -------------------------------------------------------------------------------- /Sorting/Animations/Insertion Sort.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Sorting/Animations/Insertion Sort.gif -------------------------------------------------------------------------------- /Sorting/Animations/Selection Sort 2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Sorting/Animations/Selection Sort 2.gif -------------------------------------------------------------------------------- /Sorting/Animations/Selection Sort.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Sorting/Animations/Selection Sort.gif -------------------------------------------------------------------------------- /Searching/Animations/Depth-First Search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Searching/Animations/Depth-First Search.gif -------------------------------------------------------------------------------- /Searching/Animations/Breadth-First Search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibsjose/cpp-cheat-sheet/HEAD/Searching/Animations/Breadth-First Search.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++ and Data Structures & Algorithms Cheat Sheet 2 | 3 | These are two cheat sheets I put together describing both basic [C++ syntax](C++%20Syntax.md) (mostly C++11) and many common [data structures and algorithms](Data%20Structures%20and%20Algorithms.md) in C++, which I've used to study for my past interviews at Google, NASA, etc. 4 | 5 | Hopefully you find them useful, and please open an issue or submit a PR if you find incorrect or missing information! 6 | 7 | P.S. - I didn't get the job at Google :wink: 8 | 9 | ## Contributors 10 | Many thanks to the following for their contributions: 11 | 12 | * [B1Z0N](https://github.com/B1Z0N) 13 | * [dichen001](https://github.com/dichen001) 14 | * [tsaoyu](https://github.com/tsaoyu) 15 | * [srashee](https://github.com/srashee) 16 | * [emadpres](https://github.com/emadpres) 17 | * [AurelienLourot](https://github.com/AurelienLourot) 18 | * [josephharrington](https://github.com/josephharrington) 19 | * [MuruganViswanathan](https://github.com/MuruganViswanathan) 20 | * [Damercy](https://github.com/Damercy) 21 | * [mgoutham](https://github.com/mgoutham) 22 | -------------------------------------------------------------------------------- /NP-complete/knapsack/test_knapsack.cpp: -------------------------------------------------------------------------------- 1 | #include "knapsack.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | void test_knapsack( std::size_t capacity, 8 | const std::vector& values, 9 | const std::vector& weights, 10 | std::size_t answer ) 11 | { 12 | assert( ( Knapsack { capacity, values, weights } )() == answer ); 13 | } 14 | 15 | int main() 16 | { 17 | // Base cases 18 | test_knapsack( 10, { 60 }, { 10 }, 60 ); 19 | test_knapsack( 5, { 60 }, { 10 }, 0 ); 20 | 21 | // Simple cases 22 | test_knapsack( 50, { 60, 100, 120 }, { 10, 20, 30 }, 220 ); 23 | test_knapsack( 75, { 25, 31, 15, 25, 4 }, { 43, 67, 25, 12, 36 }, 50 ); 24 | 25 | // Medium cases 26 | test_knapsack( 22, { 17, 32, 33, 30, 10, 4, 21, 40, 42, 5, 29, 27, 10, 35, 27 | 34, 33, 49, 47, 47, 28, 27, 5, 38, 47, 8, 9, 19, 10, 28 | 6, 45, 10, 23, 31, 32, 14, 17, 31, 45, 21, 39, 19, 7, 29 | 36, 19, 44, 27, 20, 39, 45, 18 }, 30 | { 25, 35, 43, 33, 6, 23, 3, 5, 42, 45, 19, 21, 27, 2, 31 | 11, 2, 6, 29, 32, 36, 21, 41, 37, 29, 15, 30, 19, 34, 32 | 1, 12, 31, 20, 44, 37, 1, 17, 18, 34, 22, 23, 2, 3, 33 | 30, 1, 7, 24, 39, 33, 14, 1 }, 235 ); 34 | 35 | std::cout << "All tests passed!\n"; 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /NP-complete/knapsack/knapsack.hpp: -------------------------------------------------------------------------------- 1 | #include // cin, cout 2 | #include // vector 3 | 4 | 5 | // @Problem 6 | // Given a set of items, each with a weight and a value, 7 | // determine the number of each item to include in a collection 8 | // so that the total weight is less than or equal to a given limit 9 | // and the total value is as large as possible. 10 | // 11 | // @Example 12 | // 13 | // ╔════════╦════╦═════╦═════╗ 14 | // ║ value ║ 60 ║ 100 ║ 120 ║ 15 | // ╠════════╬════╬═════╬═════╣ 16 | // ║ weight ║ 10 ║ 20 ║ 30 ║ 17 | // ╚════════╩════╩═════╩═════╝ 18 | // MAXW = 50 19 | // 20 | // Weight = 10; Value = 60; 21 | // Weight = 20; Value = 100; 22 | // Weight = 30; Value = 120; 23 | // Weight = 10 + 20; Value = 160; 24 | // Weight = 10 + 30; Value = 180; 25 | // Weight = 20 + 30; Value = 220; 26 | // Weight = 30 + 20 + 10 > 50 27 | // 28 | // Answer: 220; 29 | // 30 | // @Input format 31 | // 50 32 | // 3 33 | // 60 10 100 20 120 30 34 | // 35 | // @Output format 36 | // 220 37 | 38 | #ifndef KNAPSACK_HPP 39 | #define KNAPSACK_HPP 40 | 41 | 42 | /** 43 | * 0-1 knapsack solution class 44 | */ 45 | class Knapsack 46 | { 47 | using size_t = std::size_t; 48 | 49 | size_t capacity; 50 | std::vector values ; 51 | std::vector weights; 52 | 53 | bool calculated { false }; 54 | size_t result; 55 | 56 | public: 57 | 58 | Knapsack() = default; 59 | Knapsack( size_t capacity, const std::vector& values, const std::vector& weights) 60 | : capacity{ capacity }, values{ values }, weights{ weights } { } 61 | 62 | size_t get_capacity() { return capacity; } 63 | std::vector get_values () { return values ; } 64 | std::vector get_weights() { return weights; } 65 | 66 | /** 67 | * Solved only first time, for others just use saved value 68 | */ 69 | size_t operator()() 70 | { 71 | return calculated ? result : calculated = true, solve(); 72 | } 73 | 74 | friend std::ostream& operator<<( std::ostream& os, Knapsack& knp ); 75 | friend std::istream& operator>>( std::istream& is, Knapsack& knp ); 76 | 77 | private: 78 | 79 | /** 80 | * Algorithm implementation 81 | */ 82 | size_t solve(); 83 | }; 84 | 85 | 86 | #endif // KNAPSACK_HPP -------------------------------------------------------------------------------- /NP-complete/knapsack/knapsack.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 0-1 Knapsack Problem 3 | -------------------- 4 | Given weights and values of n items, put these items in a knapsack 5 | of a fixed capacity to get the maximum total value in the knapsack. 6 | 7 | Time complexity 8 | --------------- 9 | O(N*W), where N is the number of items and W is the capacity of the knapsack. 10 | 11 | Space complexity 12 | ---------------- 13 | O(N*W), the variables same as time complexity. 14 | 15 | Author 16 | ------ 17 | Fedurko Nikolaus (@B1Z0N) 18 | */ 19 | 20 | 21 | #include "knapsack.hpp" 22 | 23 | #include // cin, cout 24 | #include // vector 25 | 26 | 27 | std::ostream& operator<<( std::ostream& os, Knapsack& knp ) 28 | { 29 | return os << knp(); 30 | } 31 | 32 | 33 | std::istream& operator>>( std::istream& is, Knapsack& knp ) 34 | { 35 | is >> knp.capacity; 36 | 37 | std::size_t n, value, weight; 38 | is >> n; 39 | 40 | while( n-- ) 41 | { 42 | is >> value >> weight; 43 | 44 | knp.values.push_back(value); 45 | knp.weights.push_back(weight); 46 | } 47 | 48 | return is; 49 | } 50 | 51 | 52 | /** 53 | * Algorithm implementation 54 | */ 55 | std::size_t Knapsack::solve() 56 | { 57 | std::size_t size { values.size( ) }; 58 | std::size_t remember[ size + 1 ][ capacity + 1 ]; 59 | /** 60 | * A row number i represents the set of all the items 61 | * from rows 1 to i. For instance, the values in row 3 assumes 62 | * that we only have items 1, 2, and 3. 63 | * 64 | * A column number j represents the weight capacity of our knapsack. 65 | * Therefore, the values in column 5, for example, assumes that our 66 | * knapsack can hold 5 weight units. 67 | * 68 | * Putting everything together, an entry in row i, column j represents 69 | * the maximum value that can be obtained with items 1, 2, 3 … i, in a 70 | * knapsack that can hold j weight units. 71 | */ 72 | 73 | for( int i = 0; i <= size; ++i ) 74 | { 75 | for( int w = 0; w <= capacity; ++w ) 76 | { 77 | if( i == 0 || w == 0 ) 78 | // base case 79 | remember[ i ][ w ] = 0; 80 | else if( weights[ i - 1 ] <= w ) 81 | remember[ i ][ w ] = std::max( 82 | values[ i - 1 ] + remember[ i - 1 ][ w - weights[ i - 1 ] ], 83 | // include this item 84 | remember[ i - 1 ][ w ] // or 85 | // don't include 86 | ); 87 | else 88 | remember[ i ][ w ] = remember[ i - 1 ][ w ]; 89 | } 90 | } 91 | 92 | return remember[ size ][ capacity ]; 93 | } -------------------------------------------------------------------------------- /Data Structures and Algorithms.md: -------------------------------------------------------------------------------- 1 | # C++ Data Structures and Algorithms Cheat Sheet 2 | 3 | ## Table of Contents 4 | 5 | 6 | 7 | - [C++ Data Structures and Algorithms Cheat Sheet](#c-data-structures-and-algorithms-cheat-sheet) 8 | - [Table of Contents](#table-of-contents) 9 | - [1.0 Data Structures](#10-data-structures) 10 | - [1.1 Overview](#11-overview) 11 | - [1.2 Vector `std::vector`](#12-vector-stdvector) 12 | - [1.3 Deque `std::deque`](#13-deque-stddeque) 13 | - [1.4 List `std::list` and `std::forward_list`](#14-list-stdlist-and-stdforward_list) 14 | - [1.5 Map `std::map` and `std::unordered_map`](#15-map-stdmap-and-stdunordered_map) 15 | - [1.6 Set `std::set`](#16-set-stdset) 16 | - [1.7 Stack `std::stack`](#17-stack-stdstack) 17 | - [1.8 Queue `std::queue`](#18-queue-stdqueue) 18 | - [1.9 Priority Queue `std::priority_queue`](#19-priority-queue-stdpriority_queue) 19 | - [1.10 Heap `std::priority_queue`](#110-heap-stdpriority_queue) 20 | - [2.0 Trees](#20-trees) 21 | - [2.1 Binary Tree](#21-binary-tree) 22 | - [2.2 Balanced Trees](#22-balanced-trees) 23 | - [2.3 Binary Search](#23-binary-search) 24 | - [2.4 Depth-First Search](#24-depth-first-search) 25 | - [2.5 Breadth-First Search](#25-breadth-first-search) 26 | - [3.0 NP Complete Problems](#30-np-complete-problems) 27 | - [3.1 NP Complete](#31-np-complete) 28 | - [3.2 Traveling Salesman Problem](#32-traveling-salesman-problem) 29 | - [3.3 Knapsack Problem](#33-knapsack-problem) 30 | - [4.0 Algorithms](#40-algorithms) 31 | - [4.1 Insertion Sort](#41-insertion-sort) 32 | - [4.2 Selection Sort](#42-selection-sort) 33 | - [4.3 Bubble Sort](#43-bubble-sort) 34 | - [4.4 Merge Sort](#44-merge-sort) 35 | - [4.5 Quicksort](#45-quicksort) 36 | 37 | 38 | 39 | 40 | ## 1.0 Data Structures 41 | ### 1.1 Overview 42 | 43 | ![Legend](General/Legend.png) 44 | 45 | ![DataStructures](General/Data%20Structures.png "Data Structures") 46 | 47 | ![ComplexityChart](General/Complexity%20Chart.png "Complexity Chart") 48 | 49 | ![DataStructureSelection](General/Data%20Structures%20Selection.png "Data Structures Selection") 50 | ------------------------------------------------------- 51 | ### 1.2 Vector `std::vector` 52 | **Use for** 53 | * Simple storage 54 | * Adding but not deleting 55 | * Serialization 56 | * Quick lookups by index 57 | * Easy conversion to C-style arrays 58 | * Efficient traversal (contiguous CPU caching) 59 | 60 | **Do not use for** 61 | * Insertion/deletion in the middle of the list 62 | * Dynamically changing storage 63 | * Non-integer indexing 64 | 65 | **Time Complexity** 66 | 67 | | Operation | Time Complexity | 68 | |--------------|-----------------| 69 | | Insert Head | `O(n)` | 70 | | Insert Index | `O(n)` | 71 | | Insert Tail | `O(1)` | 72 | | Remove Head | `O(n)` | 73 | | Remove Index | `O(n)` | 74 | | Remove Tail | `O(1)` | 75 | | Find Index | `O(1)` | 76 | | Find Object | `O(n)` | 77 | 78 | **Example Code** 79 | ```c++ 80 | std::vector v; 81 | 82 | //--------------------------------- 83 | // General Operations 84 | //--------------------------------- 85 | 86 | // Size 87 | unsigned int size = v.size(); 88 | 89 | // Insert head, index, tail 90 | v.insert(v.begin(), value); // head 91 | v.insert(v.begin() + index, value); // index 92 | v.push_back(value); // tail 93 | 94 | // Access head, index, tail 95 | int head = v.front(); // head 96 | head = v[0]; // or using array style indexing 97 | 98 | int value = v.at(index); // index 99 | value = v[index]; // or using array style indexing 100 | 101 | int tail = v.back(); // tail 102 | tail = v[v.size() - 1]; // or using array style indexing 103 | 104 | // Iterate 105 | for(std::vector::iterator it = v.begin(); it != v.end(); it++) { 106 | std::cout << *it << std::endl; 107 | } 108 | 109 | // Remove head, index, tail 110 | v.erase(v.begin()); // head 111 | v.erase(v.begin() + index); // index 112 | v.pop_back(); // tail 113 | 114 | // Clear 115 | v.clear(); 116 | ``` 117 | ------------------------------------------------------- 118 | ### 1.3 Deque `std::deque` 119 | **Use for** 120 | * Similar purpose of `std::vector` 121 | * Basically `std::vector` with efficient `push_front` and `pop_front` 122 | 123 | **Do not use for** 124 | * C-style contiguous storage (not guaranteed) 125 | 126 | **Notes** 127 | * Pronounced 'deck' 128 | * Stands for **D**ouble **E**nded **Que**ue 129 | 130 | **Time Complexity** 131 | 132 | | Operation | Time Complexity | 133 | |--------------|-----------------| 134 | | Insert Head | `O(1)` | 135 | | Insert Index | `O(n) or O(1)`| 136 | | Insert Tail | `O(1)` | 137 | | Remove Head | `O(1)` | 138 | | Remove Index | `O(n)` | 139 | | Remove Tail | `O(1)` | 140 | | Find Index | `O(1)` | 141 | | Find Object | `O(n)` | 142 | 143 | **Example Code** 144 | ```c++ 145 | std::deque d; 146 | 147 | //--------------------------------- 148 | // General Operations 149 | //--------------------------------- 150 | 151 | // Insert head, index, tail 152 | d.push_front(value); // head 153 | d.insert(d.begin() + index, value); // index 154 | d.push_back(value); // tail 155 | 156 | // Access head, index, tail 157 | int head = d.front(); // head 158 | int value = d.at(index); // index 159 | int tail = d.back(); // tail 160 | 161 | // Size 162 | unsigned int size = d.size(); 163 | 164 | // Iterate 165 | for(std::deque::iterator it = d.begin(); it != d.end(); it++) { 166 | std::cout << *it << std::endl; 167 | } 168 | 169 | // Remove head, index, tail 170 | d.pop_front(); // head 171 | d.erase(d.begin() + index); // index 172 | d.pop_back(); // tail 173 | 174 | // Clear 175 | d.clear(); 176 | ``` 177 | ------------------------------------------------------- 178 | ### 1.4 List `std::list` and `std::forward_list` 179 | **Use for** 180 | * Insertion into the middle/beginning of the list 181 | * Efficient sorting (pointer swap vs. copying) 182 | 183 | **Do not use for** 184 | * Direct access 185 | 186 | **Time Complexity** 187 | 188 | | Operation | Time Complexity | 189 | |--------------|-----------------| 190 | | Insert Head | `O(1)` | 191 | | Insert Index | `O(n)` | 192 | | Insert Tail | `O(1)` | 193 | | Remove Head | `O(1)` | 194 | | Remove Index | `O(n)` | 195 | | Remove Tail | `O(1)` | 196 | | Find Index | `O(n)` | 197 | | Find Object | `O(n)` | 198 | 199 | **Example Code** 200 | ```c++ 201 | std::list l; 202 | 203 | //--------------------------------- 204 | // General Operations 205 | //--------------------------------- 206 | 207 | // Insert head, index, tail 208 | l.push_front(value); // head 209 | l.insert(l.begin() + index, value); // index 210 | l.push_back(value); // tail 211 | 212 | // Access head, index, tail 213 | int head = l.front(); // head 214 | int value = std::next(l.begin(), index); // index 215 | int tail = l.back(); // tail 216 | 217 | // Size 218 | unsigned int size = l.size(); 219 | 220 | // Iterate 221 | for(std::list::iterator it = l.begin(); it != l.end(); it++) { 222 | std::cout << *it << std::endl; 223 | } 224 | 225 | // Remove head, index, tail 226 | l.pop_front(); // head 227 | l.erase(l.begin() + index); // index 228 | l.pop_back(); // tail 229 | 230 | // Clear 231 | l.clear(); 232 | 233 | //--------------------------------- 234 | // Container-Specific Operations 235 | //--------------------------------- 236 | 237 | // Splice: Transfer elements from list to list 238 | // splice(iterator pos, list &x) 239 | // splice(iterator pos, list &x, iterator i) 240 | // splice(iterator pos, list &x, iterator first, iterator last) 241 | l.splice(l.begin() + index, list2); 242 | 243 | // Remove: Remove an element by value 244 | l.remove(value); 245 | 246 | // Unique: Remove duplicates 247 | l.unique(); 248 | 249 | // Merge: Merge two sorted lists 250 | l.merge(list2); 251 | 252 | // Sort: Sort the list 253 | l.sort(); 254 | 255 | // Reverse: Reverse the list order 256 | l.reverse(); 257 | ``` 258 | ------------------------------------------------------- 259 | ### 1.5 Map `std::map` and `std::unordered_map` 260 | **Use for** 261 | * Key-value pairs 262 | * Constant lookups by key 263 | * Searching if key/value exists 264 | * Removing duplicates 265 | * `std::map` 266 | * Ordered map 267 | * `std::unordered_map` 268 | * Hash table 269 | 270 | **Do not use for** 271 | * Sorting 272 | 273 | **Notes** 274 | * Typically ordered maps (`std::map`) are slower than unordered maps (`std::unordered_map`) 275 | * Maps are typically implemented as *binary search trees* 276 | 277 | **Time Complexity** 278 | 279 | **`std::map`** 280 | 281 | | Operation | Time Complexity | 282 | |---------------------|-----------------| 283 | | Insert | `O(log(n))` | 284 | | Access by Key | `O(log(n))` | 285 | | Remove by Key | `O(log(n))` | 286 | | Find/Remove Value | `O(log(n))` | 287 | 288 | **`std::unordered_map`** 289 | 290 | | Operation | Time Complexity | 291 | |---------------------|-----------------| 292 | | Insert | `O(1)` | 293 | | Access by Key | `O(1)` | 294 | | Remove by Key | `O(1)` | 295 | | Find/Remove Value | -- | 296 | 297 | **Example Code** 298 | ```c++ 299 | std::map m; 300 | 301 | //--------------------------------- 302 | // General Operations 303 | //--------------------------------- 304 | 305 | // Insert 306 | m.insert(std::pair("key", "value")); 307 | 308 | // Access by key 309 | std::string value = m.at("key"); 310 | 311 | // Size 312 | unsigned int size = m.size(); 313 | 314 | // Iterate 315 | for(std::map::iterator it = m.begin(); it != m.end(); it++) { 316 | std::cout << (*it).first << " " << (*it).second << std::endl; 317 | } 318 | 319 | // Remove by key 320 | m.erase("key"); 321 | 322 | // Clear 323 | m.clear(); 324 | 325 | //--------------------------------- 326 | // Container-Specific Operations 327 | //--------------------------------- 328 | 329 | // Find if an element exists by key 330 | bool exists = (m.find("key") != m.end()); 331 | 332 | // Count the number of elements with a certain key 333 | unsigned int count = m.count("key"); 334 | ``` 335 | ------------------------------------------------------- 336 | ### 1.6 Set `std::set` 337 | **Use for** 338 | * Removing duplicates 339 | * Ordered dynamic storage 340 | 341 | **Do not use for** 342 | * Simple storage 343 | * Direct access by index 344 | 345 | **Notes** 346 | * Sets are often implemented with binary search trees 347 | 348 | **Time Complexity** 349 | 350 | | Operation | Time Complexity | 351 | |--------------|-----------------| 352 | | Insert | `O(log(n))` | 353 | | Remove | `O(log(n))` | 354 | | Find | `O(log(n))` | 355 | 356 | **Example Code** 357 | ```c++ 358 | std::set s; 359 | 360 | //--------------------------------- 361 | // General Operations 362 | //--------------------------------- 363 | 364 | // Insert 365 | s.insert(20); 366 | 367 | // Size 368 | unsigned int size = s.size(); 369 | 370 | // Iterate 371 | for(std::set::iterator it = s.begin(); it != s.end(); it++) { 372 | std::cout << *it << std::endl; 373 | } 374 | 375 | // Remove 376 | s.erase(20); 377 | 378 | // Clear 379 | s.clear(); 380 | 381 | //--------------------------------- 382 | // Container-Specific Operations 383 | //--------------------------------- 384 | 385 | // Find if an element exists 386 | bool exists = (s.find(20) != s.end()); 387 | 388 | // Count the number of elements with a certain value 389 | unsigned int count = s.count(20); 390 | ``` 391 | ------------------------------------------------------- 392 | ### 1.7 Stack `std::stack` 393 | **Use for** 394 | * First-In Last-Out operations 395 | * Reversal of elements 396 | 397 | **Time Complexity** 398 | 399 | | Operation | Time Complexity | 400 | |--------------|-----------------| 401 | | Push | `O(1)` | 402 | | Pop | `O(1)` | 403 | | Top | `O(1)` | 404 | 405 | **Example Code** 406 | ```c++ 407 | std::stack s; 408 | 409 | //--------------------------------- 410 | // Container-Specific Operations 411 | //--------------------------------- 412 | 413 | // Push 414 | s.push(20); 415 | 416 | // Size 417 | unsigned int size = s.size(); 418 | 419 | // Pop 420 | s.pop(); 421 | 422 | // Top 423 | int top = s.top(); 424 | ``` 425 | ------------------------------------------------------- 426 | ### 1.8 Queue `std::queue` 427 | **Use for** 428 | * First-In First-Out operations 429 | * Ex: Simple online ordering system (first come first served) 430 | * Ex: Semaphore queue handling 431 | * Ex: CPU scheduling (FCFS) 432 | 433 | **Notes** 434 | * Often implemented as a `std::deque` 435 | 436 | **Example Code** 437 | ```c++ 438 | std::queue q; 439 | 440 | //--------------------------------- 441 | // General Operations 442 | //--------------------------------- 443 | 444 | // Insert 445 | q.push(value); 446 | 447 | // Access head, tail 448 | int head = q.front(); // head 449 | int tail = q.back(); // tail 450 | 451 | // Size 452 | unsigned int size = q.size(); 453 | 454 | // Remove 455 | q.pop(); 456 | ``` 457 | ------------------------------------------------------- 458 | ### 1.9 Priority Queue `std::priority_queue` 459 | **Use for** 460 | * First-In First-Out operations where **priority** overrides arrival time 461 | * Ex: CPU scheduling (smallest job first, system/user priority) 462 | * Ex: Medical emergencies (gunshot wound vs. broken arm) 463 | 464 | **Notes** 465 | * Often implemented as a `std::vector` 466 | 467 | **Example Code** 468 | ```c++ 469 | std::priority_queue p; 470 | 471 | //--------------------------------- 472 | // General Operations 473 | //--------------------------------- 474 | 475 | // Insert 476 | p.push(value); 477 | 478 | // Access 479 | int top = p.top(); // 'Top' element 480 | 481 | // Size 482 | unsigned int size = p.size(); 483 | 484 | // Remove 485 | p.pop(); 486 | ``` 487 | ------------------------------------------------------- 488 | ### 1.10 Heap `std::priority_queue` 489 | **Notes** 490 | * A heap is essentially an instance of a priority queue 491 | * A **min** heap is structured with the root node as the smallest and each child subsequently larger than its parent 492 | * A **max** heap is structured with the root node as the largest and each child subsequently smaller than its parent 493 | * A min heap could be used for *Smallest Job First* CPU Scheduling 494 | * A max heap could be used for *Priority* CPU Scheduling 495 | 496 | **Max Heap Example (using a binary tree)** 497 | 498 | ![MaxHeap](General/MaxHeap.png) 499 | ------------------------------------------------------- 500 | ## 2.0 Trees 501 | ### 2.1 Binary Tree 502 | * A binary tree is a tree with at most two (2) child nodes per parent 503 | * Binary trees are commonly used for implementing `O(log(n))` operations for ordered maps, sets, heaps, and binary search trees 504 | * Binary trees are **sorted** in that nodes with values greater than their parents are inserted to the **right**, while nodes with values less than their parents are inserted to the **left** 505 | 506 | **Binary Search Tree** 507 | 508 | ![BinarySearchTree](General/BinarySearchTree.png) 509 | ------------------------------------------------------- 510 | ### 2.2 Balanced Trees 511 | * Balanced trees are a special type of tree which maintains its balance to ensure `O(log(n))` operations 512 | * When trees are not balanced the benefit of `log(n)` operations is lost due to the highly vertical structure 513 | * Examples of balanced trees: 514 | * AVL Trees 515 | * Red-Black Trees 516 | 517 | ------------------------------------------------------- 518 | ### 2.3 Binary Search 519 | **Idea:** 520 | 1. If current element, return 521 | 2. If less than current element, look left 522 | 3. If more than current element, look right 523 | 4. Repeat 524 | 525 | **Data Structures:** 526 | * Tree 527 | * Sorted array 528 | 529 | **Space:** 530 | * `O(1)` 531 | 532 | **Best Case:** 533 | * `O(1)` 534 | 535 | **Worst Case:** 536 | * `O(log n)` 537 | 538 | **Average:** 539 | * `O(log n)` 540 | 541 | **Visualization:** 542 | 543 | ![BinarySearch](Searching/Animations/Binary%20Search.gif "Binary Search") 544 | ------------------------------------------------------- 545 | ### 2.4 Depth-First Search 546 | **Idea:** 547 | 1. Start at root node 548 | 2. Recursively search all adjacent nodes and mark them as searched 549 | 3. Repeat 550 | 551 | **Data Structures:** 552 | * Tree 553 | * Graph 554 | 555 | **Space:** 556 | * `O(V)`, `V = number of verticies` 557 | 558 | **Performance:** 559 | * `O(E)`, `E = number of edges` 560 | 561 | **Visualization:** 562 | 563 | ![DepthFirstSearch](Searching/Animations/Depth-First%20Search.gif "Depth-First Search") 564 | ------------------------------------------------------- 565 | ### 2.5 Breadth-First Search 566 | **Idea:** 567 | 1. Start at root node 568 | 2. Search neighboring nodes first before moving on to next level 569 | 570 | **Data Structures:** 571 | * Tree 572 | * Graph 573 | 574 | **Space:** 575 | * `O(V)`, `V = number of verticies` 576 | 577 | **Performance:** 578 | * `O(E)`, `E = number of edges` 579 | 580 | **Visualization:** 581 | 582 | ![DepthFirstSearch](Searching/Animations/Breadth-First%20Search.gif "Breadth-First Search") 583 | ------------------------------------------------------- 584 | ## 3.0 NP Complete Problems 585 | ### 3.1 NP Complete 586 | * **NP Complete** means that a problem is unable to be solved in **polynomial time** 587 | * NP Complete problems can be *verified* in polynomial time, but not *solved* 588 | 589 | ------------------------------------------------------- 590 | ### 3.2 Traveling Salesman Problem 591 | 592 | ------------------------------------------------------- 593 | ### 3.3 Knapsack Problem 594 | 595 | [Implementation](NP-complete/knapsack/) 596 | 597 | ------------------------------------------------------- 598 | 599 | ## 4.0 Algorithms 600 | ### 4.1 Insertion Sort 601 | #### Idea 602 | 1. Iterate over all elements 603 | 2. For each element: 604 | * Check if element is larger than largest value in sorted array 605 | 3. If larger: Move on 606 | 4. If smaller: Move item to correct position in sorted array 607 | 608 | #### Details 609 | * **Data structure:** Array 610 | * **Space:** `O(1)` 611 | * **Best Case:** Already sorted, `O(n)` 612 | * **Worst Case:** Reverse sorted, `O(n^2)` 613 | * **Average:** `O(n^2)` 614 | 615 | #### Advantages 616 | * Easy to code 617 | * Intuitive 618 | * Better than selection sort and bubble sort for small data sets 619 | * Can sort in-place 620 | 621 | #### Disadvantages 622 | * Very inefficient for large datasets 623 | 624 | #### Visualization 625 | 626 | ![InsertionSort](Sorting/Animations/Insertion%20Sort.gif "Insertion Sort") 627 | ------------------------------------------------------- 628 | ### 4.2 Selection Sort 629 | #### Idea 630 | 1. Iterate over all elements 631 | 2. For each element: 632 | * If smallest element of unsorted sublist, swap with left-most unsorted element 633 | 634 | #### Details 635 | * **Data structure:** Array 636 | * **Space:** `O(1)` 637 | * **Best Case:** Already sorted, `O(n^2)` 638 | * **Worst Case:** Reverse sorted, `O(n^2)` 639 | * **Average:** `O(n^2)` 640 | 641 | #### Advantages 642 | * Simple 643 | * Can sort in-place 644 | * Low memory usage for small datasets 645 | 646 | #### Disadvantages 647 | * Very inefficient for large datasets 648 | 649 | #### Visualization 650 | 651 | ![SelectionSort](Sorting/Animations/Selection%20Sort.gif "Selection Sort") 652 | 653 | ![SelectionSort](Sorting/Animations/Selection%20Sort%202.gif "Selection Sort 2") 654 | ------------------------------------------------------- 655 | ### 4.3 Bubble Sort 656 | #### Idea 657 | 1. Iterate over all elements 658 | 2. For each element: 659 | * Swap with next element if out of order 660 | 3. Repeat until no swaps needed 661 | 662 | #### Details 663 | * **Data structure:** Array 664 | * **Space:** `O(1)` 665 | * **Best Case:** Already sorted `O(n)` 666 | * **Worst Case:** Reverse sorted, `O(n^2)` 667 | * **Average:** `O(n^2)` 668 | 669 | #### Advantages 670 | * Easy to detect if list is sorted 671 | 672 | #### Disadvantages 673 | * Very inefficient for large datasets 674 | * Much worse than even insertion sort 675 | 676 | #### Visualization 677 | 678 | ![BubbleSort](Sorting/Animations/Bubble%20Sort.gif "Bubble Sort") 679 | ------------------------------------------------------- 680 | ### 4.4 Merge Sort 681 | #### Idea 682 | 1. Divide list into smallest unit (1 element) 683 | 2. Compare each element with the adjacent list 684 | 3. Merge the two adjacent lists 685 | 4. Repeat 686 | 687 | #### Details 688 | * **Data structure:** Array 689 | * **Space:** `O(n) auxiliary` 690 | * **Best Case:** `O(nlog(n))` 691 | * **Worst Case:** Reverse sorted, `O(nlog(n))` 692 | * **Average:** `O(nlog(n))` 693 | 694 | #### Advantages 695 | * High efficiency on large datasets 696 | * Nearly always O(nlog(n)) 697 | * Can be parallelized 698 | * Better space complexity than standard Quicksort 699 | 700 | #### Disadvantages 701 | * Still requires O(n) extra space 702 | * Slightly worse than Quicksort in some instances 703 | 704 | #### Visualization 705 | 706 | ![MergeSort](Sorting/Animations/Merge%20Sort.gif "Merge Sort") 707 | 708 | ![MergeSort](Sorting/Animations/Merge%20Sort%202.gif "Merge Sort 2") 709 | ------------------------------------------------------- 710 | ### 4.5 Quicksort 711 | #### Idea 712 | 1. Choose a **pivot** from the array 713 | 2. Partition: Reorder the array so that all elements with values *less* than the pivot come before the pivot, and all values *greater* than the pivot come after 714 | 3. Recursively apply the above steps to the sub-arrays 715 | 716 | #### Details 717 | * **Data structure:** Array 718 | * **Space:** `O(n)` 719 | * **Best Case:** `O(nlog(n))` 720 | * **Worst Case:** All elements equal, `O(n^2)` 721 | * **Average:** `O(nlog(n))` 722 | 723 | #### Advantages 724 | * Can be modified to use O(log(n)) space 725 | * Very quick and efficient with large datasets 726 | * Can be parallelized 727 | * Divide and conquer algorithm 728 | 729 | #### Disadvantages 730 | * Not stable (could swap equal elements) 731 | * Worst case is worse than Merge Sort 732 | 733 | #### Optimizations 734 | * Choice of pivot: 735 | * Choose median of the first, middle, and last elements as pivot 736 | * Counters worst-case complexity for already-sorted and reverse-sorted 737 | 738 | #### Visualization 739 | 740 | ![QuickSort](Sorting/Animations/Quicksort.gif) 741 | -------------------------------------------------------------------------------- /C++ Syntax.md: -------------------------------------------------------------------------------- 1 | # C++ Syntax Cheat Sheet 2 | 3 | ## Preface 4 | Since the C++ language varies so heavily between versions (e.g. C++0x, C++11, C++17, etc.), I will preface this cheat sheet by saying that the majority of the examples here target C++0x or c++11, as those are the versions that I am most familiar with. I come from the aerospace industry (embedded flight software) in which we purposefully don't use the latest technologies for safety reasons, so most of the code I write is in C++0x and sometimes C++11. Nevertheless, the basic concepts of C++ and object oriented programming still generally apply to both past and future versions of the language. 5 | 6 | ## Table of Contents 7 | 8 | 9 | 10 | - [C++ Syntax Cheat Sheet](#c-syntax-cheat-sheet) 11 | - [Table of Contents](#table-of-contents) 12 | - [1.0 C++ Classes](#10-c-classes) 13 | - [1.1 Class Syntax](#11-class-syntax) 14 | - [1.1.1 Class Declaration (`.h` file)](#111-class-declaration-h-file) 15 | - [1.1.2 Class Definition (`.cpp` file)](#112-class-definition-cpp-file) 16 | - [1.1.3 Class Utilization (Another `.cpp` file)](#113-class-utilization-another-cpp-file) 17 | - [1.1.4 Getters and Setters](#114-getters-and-setters) 18 | - [1.2 Inheritance](#12-inheritance) 19 | - [1.2.1 `Rectangle` Declaration (`.h` file)](#121-rectangle-declaration-h-file) 20 | - [1.2.2 `Rectangle` Definition (`.cpp` file)](#122-rectangle-definition-cpp-file) 21 | - [1.2.3 `Rectangle` Utilization (Another `.cpp` file)](#123-rectangle-utilization-another-cpp-file) 22 | - [1.3 Class Polymorphism](#13-class-polymorphism) 23 | - [1.3.1 Motivation](#131-motivation) 24 | - [1.3.2 Virtual Methods](#132-virtual-methods) 25 | - [1.4 Special Methods (Constructor, Destructor, ...)](#14-special-methods) 26 | - [1.4.1 Constructor and Destructor](#141-constructor-and-destructor) 27 | - [1.4.1.1 Use of `explicit` in Constructors](#1411-use-of-explicit-in-constructors) 28 | - [1.4.1.2 Member Initializer List](#1412-member-initializer-list) 29 | - [1.4.2. `new` and `delete`](#142-new-and-delete) 30 | - [1.4.3. Copy Constructor and Copy Assignment](#143-copy-constructor-and-copy-assignment) 31 | - [1.4.4. Move Constructor and Move Assignment](#144-move-constructor-and-move-assignment) 32 | - [1.5 Operator Overloading](#15-operator-overloading) 33 | - [1.6 Templates](#16-templates) 34 | - [2.0 General C++ Syntax](#20-general-c-syntax) 35 | - [2.1 Namespaces](#21-namespaces) 36 | - [2.2 References/Pointers](#22-references-and-pointers) 37 | - [2.3 Keywords](#23-keywords) 38 | - [2.3.1 General keywords](#231-general-keywords) 39 | - [2.3.2 Storage class specifiers](#232-storage-class-specifiers) 40 | - [2.3.3 `const` and `dynamic` Cast Conversion](#233-const-and-dynamic-cast-conversion) 41 | - [2.4 Preprocessor Tokens](#24-preprocessor-tokens) 42 | - [2.5 Strings ](#25-strings-stdstring) 43 | - [2.6 Iterators](#26-iterators-stditerator) 44 | - [2.7 Exceptions](#27-exceptions) 45 | - [2.8 Lambdas](#28-lambdas) 46 | 47 | 48 | 49 | 50 | ## 1.0 C++ Classes 51 | ### 1.1 Class Syntax 52 | #### 1.1.1 Class Declaration (`.h` file) 53 | Here's a simple class representing a polygon, a shape with any number of sides. 54 | 55 | The class *declaration* typically goes in the header file, which has the extension `.h` (or, less commonly, `.hpp` to distinguish from C headers). The *declaration* gives the class name, any classes it may extend, declares the members and methods, and declares which members/methods are public, private, or protected. You can think of the declaration as sort of saying: "there will be a thing and here's how it will look like". The declaration is used to inform the compiler about the future essence and use of a particular symbol. 56 | 57 | ```c++ 58 | // File: polygon.h 59 | 60 | #include 61 | 62 | class Polygon { 63 | 64 | // Private members and methods are only accessible via methods in the class definition 65 | private: 66 | int num_sides; // Number of sides 67 | 68 | // Protected members and methods are only accessible in the class definition or by classes who extend this class 69 | protected: 70 | std::string name; // Name of the polygon 71 | 72 | // Public members and methods are accessible to anyone who creates an instance of the class 73 | public: 74 | // Constructors 75 | Polygon(const int num_sides, const std::string & name); // <--- This constructor takes the number of sides and name as arguments 76 | 77 | // Getters and Setters 78 | int GetNumSides(void) const; 79 | void SetNumSides(const int num_sides); 80 | 81 | std::string & GetName(void) const; 82 | void SetName(const std::string & name); 83 | 84 | }; // <--- Don't forget the semicolon! 85 | ``` 86 | 87 | #### 1.1.2 Class Definition (`.cpp` file) 88 | The class *definition* typically goes in the `.cpp` file. The *definition* extends the declaration by providing an actual implementation of whatever it is that you're building. Continuing the example from the declaration, the definition can be thought of as saying: "Right, that thing I told you briefly about earlier? Here's how it actually functions". The definition thus provides the compileable implementation. 89 | 90 | ```c++ 91 | // File: polygon.cpp 92 | 93 | #include // <--- Required for std::string 94 | 95 | #include "polygon.h" // <--- Obtains the class declaration 96 | 97 | // Constructor 98 | // You must scope the method definitions with the class name (Polygon::) 99 | // Also, see the section on the 'explicit' keyword for a warning about constructors with exactly one argument 100 | Polygon::Polygon(const int num_sides, const std::string & name) { 101 | this->num_sides = num_sides; // 'this' is a pointer to the instance of the class. Members are accessed via the -> operator 102 | this->name = name; // In this case you need to use 'this->...' to avoid shadowing the member variable since the argument shares the same name 103 | } 104 | 105 | // Get the number of sides 106 | int Polygon::GetNumSides(void) const { // The 'const' here tells the compiler that you guarantee that you won't modify the object when this function is called. This allows it to perform optimizations that it otherwise may not be able to do 107 | return this->num_sides; 108 | } 109 | 110 | // Set the number of sides 111 | void Polygon::SetNumSides(const int num_sides) { 112 | this->num_sides = num_sides; 113 | } 114 | 115 | // Get the polygon name 116 | std::string & Polygon::GetName(void) const { 117 | return this->name; 118 | } 119 | 120 | // Set the polygon name 121 | void Polygon::SetName(const std::string & name) { 122 | this->name = name; 123 | } 124 | ``` 125 | 126 | The getters and setters here don't do much, but you could imagine limiting the number of sides such that it must have at least 3 sides to be a useful polygon, in which case you could enforce that in `Polygon::SetNumSides()`. Of course, you'd also need to modify the constructor, which could then call `SetNumSides()` instead of setting the variable directly. 127 | 128 | > NOTE: Regarding the use of `this->` in a class definition, there are places where it's strictly necessary for readability, e.g. when your method parameter shares the exact same name as a member variable, you use `this->` to avoid what's called shadowing. However, some prefer to always use `this->` explicitly regardless of whether it's necessary. 129 | 130 | #### 1.1.3 Class Utilization (Another `.cpp` file) 131 | ```c++ 132 | // File: main.cpp 133 | 134 | #include 135 | #include 136 | 137 | #include "Polygon.h" // <--- Obtains the class declaration 138 | 139 | int main(int argc, char * argv[]) { 140 | // Create a polygon with 4 sides and the name "Rectangle" 141 | Polygon polygon = Polygon(4, "Rectangle"); 142 | 143 | // Check number of sides -- Prints "Rectangle has 4 sides" 144 | std::cout << polygon.GetName() << " has " << polygon.GetNumSides() << " sides"<< std::endl; 145 | 146 | // Change number of sides to 3 and rename to "Triangle" 147 | polygon.SetNumSides(3); 148 | polygon.SetName("Triangle"); 149 | } 150 | ``` 151 | 152 | #### 1.1.4 Getters and Setters 153 | A shortcut often used for Getters/Setters is to define them in the class declaration (`.h`) file as follows: 154 | ```c++ 155 | // File: car.h 156 | 157 | #include 158 | 159 | class Car { 160 | private: 161 | int year; 162 | std::string make; 163 | 164 | public: 165 | int GetYear(void) const { return this->year; } 166 | void SetYear(const int year) { this->year = year; } 167 | std::string & GetMake(void) const { return this->make; } 168 | void SetMake(const std::string & make) { this->make = make; } 169 | }; 170 | ``` 171 | 172 | This is often used for very basic getters and setters, and also for basic constructors. In contrast, you'll nearly always find more complex methods defined in the `.cpp` file. One exception to this is with class templates, in which the entire templated class declaration and definition must reside in the header file. 173 | 174 | Another important consideration: If you have getters and setters for all of your members, you may want to reconsider the design of your class. Sometimes having getters and setters for every member is indicative of poor planning of the class design and interface. In particular, setters should be used more thoughtfully. Could a variable be set once in the constructor and left constant thereafter? Does it need to be modified at all? Is it set somewhere else in another method, perhaps even indirectly? 175 | 176 | ### 1.2 Inheritance 177 | A class can extend another class, meaning that the new class inherits all of the data from the other class, and can also override its methods, add new members, etc. Inheritance is the key feature required for [polymorphism](#13-class-polymorphism). 178 | 179 | It is important to note that this feature is often overused by beginners and sometimes unnecessary hierarchies are created, adding to the overally complexity. There are some good alternatives such as [composition](https://en.wikipedia.org/wiki/Composition_over_inheritance) and [aggregation](https://stackoverflow.com/a/269535), although, of course, sometimes inheritance is exactly what is needed. 180 | 181 | **Example:** the class `Rectangle` can inherit from the class `Polygon`. You would then say that a `Rectangle` extends from a `Polygon`, or that class `Rectangle` is a sub-class of `Polygon`. In plain English, this means that a `Rectangle` is a more specialized version of a `Polygon`. Thus, all rectangles are polygons, but not all polygons are rectangles. 182 | 183 | #### 1.2.1 `Rectangle` Declaration (`.h` file) 184 | ```c++ 185 | // File: rectangle.h 186 | 187 | #include // <--- Explicitly include the string header, even though polygon.h also includes it 188 | 189 | #include "polygon.h" // <--- You must include the declaration in order to extend the class 190 | 191 | // We extend from Polygon by using the colon (:) and specifying which type of inheritance 192 | // will be used (public inheritance, in this case) 193 | 194 | class Rectangle : public Polygon { 195 | private: 196 | int length; 197 | int width; 198 | 199 | // <--- NOTE: The member variables 'num_sides' and 'name' are already inherited from Polygon 200 | // it's as if we sort of get them for free, since we are a sub-class 201 | 202 | public: 203 | // Constructors 204 | explicit Rectangle(const std::string &name); 205 | Rectangle(const std::string &name, const int length, const int width); 206 | 207 | // Getters and Setters 208 | const int GetLength(void) const { return this->length; } 209 | void SetLength(const int) { this->length = length; } 210 | 211 | const int GetWidth(void) const { return this->width; } 212 | void SetWidth(const int) { this->width = width; } 213 | 214 | // <--- NOTE: Again, the getters/setters for 'num_sides' and 'name' are already inherited from Polygon 215 | 216 | // Other Methods 217 | const int Area(void) const; 218 | }; 219 | ``` 220 | 221 | > NOTE: The inheritance access specifier (`public`, `protected`, or `private`) is used to determine the [type of inheritance](https://www.tutorialspoint.com/cplusplus/cpp_inheritance.htm). If this is omitted then `private` inheritance is used by default. **Public inheritance is by far the most common type of inheritance**. 222 | 223 | #### 1.2.2 `Rectangle` Definition (`.cpp` file) 224 | ```c++ 225 | // File: rectangle.cpp 226 | 227 | #include "rectangle.h" // <--- Only need to include 'Rectangle', since 'Polygon' is included in 'rectangle.h' 228 | 229 | // This constructor calls the superclass (Polygon) constructor and sets the name and number of sides to '4', and then sets the length and width 230 | Rectangle::Rectangle(const std::string &name, const int length, const int width) : Polygon(4, name) { 231 | this->length = length; 232 | this->width = width; 233 | } 234 | 235 | // This constructor calls the superclass (Polygon) constructor, but sets the length and width to a constant value 236 | // The explicit keyword is used to restrict the use of the constructor. See section below for more detail 237 | explicit Rectangle::Rectangle(const std::string &name) : Polygon(4, name) { 238 | this->length = 1; 239 | this->width = 1; 240 | } 241 | 242 | // Compute the area of the rectangle 243 | int Rectangle::Area(void) const { 244 | return length * width; // <--- Note that you don't explicitly need 'this->', you can directly use the member variables 245 | } 246 | ``` 247 | 248 | #### 1.2.3 `Rectangle` Utilization (Another `.cpp` file) 249 | ```c++ 250 | // File: main.cpp 251 | 252 | #include 253 | 254 | #include "Rectangle.h" 255 | 256 | int main(int argc, char *argv[]) { 257 | Rectangle rectangle = Rectangle("Square", 6, 6); 258 | 259 | // Prints "Square has 4 sides, and an area of 36" 260 | std::cout << rectangle.GetName() << " has " << rectangle.GetNumSides() << " sides, and an area of " << rectangle.Area() << std::endl; 261 | } 262 | ``` 263 | 264 | ### 1.3 Class Polymorphism 265 | Polymorphism describes a system in which a common interface is used to manipulate objects of different types. In essence various classes can inherit from a common interface through which they make certain guarantees about which methods/variables are available for use. By adhering to this common interface, one can use a pointer to an object of the base interface type to call the methods of any number of extending classes. Using polymorphism one can say "I don't care what type this really is; I know it implements `Foo()` and `Bar()` because it inherits from this interface", which is a pretty nifty feature. 266 | 267 | The `virtual` keyword is used to ensure runtime polymorphism for class methods. Additionally, an overriding method can be forced by the compiler by not providing a default implementation in the interface, which is done by setting the method to `= 0`, as will be shown later. 268 | 269 | #### 1.3.1 Motivation 270 | Let's consider a similar class hierarchy using shapes as previously discussed. Considering a shape to be any 3 or more sided polygon from which we can compute certain attributes (like the shape's area), let's extend from it to create a rectangle class from which we can set the length/width and a circle class in which you can set the radius. **In both cases, we want to be able to compute the area of the shape.** This is a key observation that we will expand upon later. 271 | 272 | For now, this (poorly implemented) shape class will suffice: 273 | 274 | ```c++ 275 | // File: shape.h 276 | 277 | #include // needed for M_PI constant 278 | 279 | class Shape { 280 | // We'll leave Shape empty for now... not very interesting yet 281 | }; 282 | 283 | class Rectangle : public Shape { 284 | private: 285 | double length; 286 | double width; 287 | 288 | public: 289 | // Constructor using a member initializer list instead of assignment in the method body 290 | Rectangle(const double w, const double l) : width(w), length(l) {} 291 | 292 | // Compute the area of a rectangle 293 | double Area(void) const { 294 | return length * width; 295 | } 296 | }; 297 | 298 | class Circle : public Shape { 299 | private: 300 | double radius; 301 | 302 | public: 303 | explicit Circle(double r) : radius(r) {} 304 | 305 | // Compute the area of a circle 306 | double Area(void) const { 307 | return M_PI * radius * radius; // pi*r^2 308 | } 309 | }; 310 | ``` 311 | 312 | > NOTE: As shown here, you can put multiple classes in a single header, although in practice unless you have a good reason for doing so it's probably best to use a separate header file per class. 313 | 314 | > NOTE: I'm not using default value initialization for member variables (i.e. `double length = 0;`) and I'm using parentheses `()` instead of braces `{}` for the initializer list since older compilers (pre-C++11) may not support the new syntax. 315 | 316 | So, we have our two classes, `Rectangle` and `Circle`, but in this case inheriting from `Shape` isn't really buying us anything. To make use of polymorphism we need to pull the common `Area()` method into the base class as follows, by using virtual methods. 317 | 318 | #### 1.3.2 Virtual Methods 319 | Imagine you want to have a pointer to a shape with which you want to compute the area of that shape. For example, maybe you want to hold shapes in some sort of data structure, but you don't want to limit yourself to just rectangles or just circles; you want to support all objects that call themselves a 'Shape'. Something like: 320 | 321 | ```c++ 322 | Rectangle rectangle(2.0, 5.0); 323 | Circle circle(1.0); 324 | 325 | // Point to the rectangle 326 | Shape * unknown_shape = &rectangle; // Could point to *any* shape, Rectangle, Circle, Triangle, Dodecagon, etc. 327 | 328 | unknown_shape->Area(); // Returns 10.0 329 | 330 | // Point to the circle 331 | unknown_shape = &circle; 332 | unknown-shape->Area(); // Returns 3.14... 333 | ``` 334 | 335 | The way to achieve this is to use the `virtual` keyword on the base class methods, which specifies that when a pointer to a base class invokes the method of an object that it points to, it should determine, at runtime, the correct method to invoke. That is, when `unknown_shape` points to a `Rectangle` it invokes `Rectangle::Area()` and if `unknown_shape` points to a `Circle` it invokes `Circle::Area()`. 336 | 337 | Virtual methods are employed as follows: 338 | 339 | ```c++ 340 | #include 341 | 342 | class Shape { 343 | public: 344 | // Virtual destructor (VERY IMPORTANT, SEE NOTE BELOW) 345 | virtual ~Shape() {} 346 | 347 | // Virtual area method 348 | virtual double Area() const { 349 | return 0.0; 350 | } 351 | }; 352 | 353 | class Rectangle : public Shape { 354 | private: 355 | double length; 356 | double width; 357 | 358 | public: 359 | Rectangle(double w, double l) : width(w), length(l) {} 360 | 361 | // Override the Shape::Area() method with an implementation specific to Rectangle 362 | double Area() const override { 363 | return length * width; 364 | } 365 | }; 366 | 367 | class Circle : public Shape { 368 | private: 369 | double radius; 370 | 371 | public: 372 | explicit Circle(double t) : radius(r) {} 373 | 374 | // Override the Shape::Area() method with an implementation specific to Circle 375 | // 376 | // NOTE: there is an 'override' keyword that was introduced in C++11 and is optional: it is used 377 | // to enforce that the method is indeed an overriding method of a virtual base method at compile time 378 | // and is used as follows: 379 | double Area() const override { 380 | return M_PI * radius * radius; // pi*r^2 381 | } 382 | }; 383 | ``` 384 | 385 | > NOTE: It is very important that a default virtual destructor was included after adding the virtual `Area()` method to the base class. Whenever a base class includes even a single virtual method, it must include a virtual destructor so that the correct destructor(s) are called in the correct order when the object is eventually deleted. 386 | 387 | This is called runtime polymorphism because the decision of which implementation of the `Area()` method to use is determined during program execution based on the type that the base is pointing at. It is implemented using [the virtual table](https://www.learncpp.com/cpp-tutorial/125-the-virtual-table/) mechanism. In a nutshell: it is a little more expensive to use but it can be immensely useful. There is also compile-time polymorphism. Here is more on the [differences between them](https://www.geeksforgeeks.org/polymorphism-in-c/). 388 | 389 | In the example above, if a class extends from `Shape` but does not include an override of `Area()` then calling the `Area()` method will invoke the base class method which (in the implementation above) returns `0.0`. 390 | 391 | In some cases, you may want to **enforce** that sub-classes implement this method. This is done by not providing a default implementation, thus making it what is called a *pure virtual* method. 392 | 393 | ```c++ 394 | class Shape { 395 | public: 396 | virtual ~Shape() {} 397 | virtual double Area() const = 0; 398 | }; 399 | ``` 400 | 401 | In general a class with only pure virtual methods and a virtual destructor is called an *abstract class* or *interface* and is typically named as such (e.g. `ButtonInterface`, or similar). An interface class guarantees that all extending classes implement a specific method with a specific method signature. 402 | 403 | ### 1.4 Special Methods 404 | #### 1.4.1 Constructor and Destructor 405 | All classes have at least one constructor and a destructor, even if they are not explicitly defined. The constructor and destructor 406 | assist in managing the lifetime of the object. The constructor is invoked when an object is created and the destructor is invoked 407 | when an object is destroyed (either by going out of scope or explicitly using `delete`). 408 | 409 | The constructor establishes a [class invariant](https://softwareengineering.stackexchange.com/a/32755), a set of assertions guaranteed to be true during the lifetime of the object, which is then removed when the destructor is called. 410 | 411 | ##### 1.4.1.1 Use of `explicit` in Constructors 412 | The `explicit` keyword should be used in single-argument constructors to avoid a situation in which the constructor is implicitly invoked when a single argument is given in place of an object. Consider the following `Array` class: 413 | 414 | ```c++ 415 | class Array { 416 | private: 417 | int size; 418 | 419 | public: 420 | // Constructor 421 | Array(int size) { 422 | this->size = size; 423 | } 424 | 425 | // Destructor 426 | ~Array() {} 427 | 428 | // Print the contents of the array 429 | Print(const Array & array) { 430 | // ... 431 | } 432 | }; 433 | ``` 434 | 435 | The following is now legal but ambiguous: 436 | ```c++ 437 | Array array = 12345; 438 | ``` 439 | 440 | It ends up being the equivalent of this: 441 | ```c++ 442 | Array array = Array(12345); 443 | ``` 444 | 445 | Perhaps that's okay, but what about the following: 446 | ```c++ 447 | array.Print(12345); 448 | ``` 449 | 450 | Uh-oh. That's now legal, compileable code, but what does it mean? It is extremely unclear to the user. 451 | 452 | To fix this, declare the single-argument `Array` constructor as `explicit`: 453 | ```c++ 454 | class Array { 455 | int size; 456 | public: 457 | explicit Array(int size) { 458 | this->size = size; 459 | } 460 | 461 | // ... 462 | }; 463 | ``` 464 | 465 | Now you can only use the print method as follows: 466 | ```c++ 467 | array.Print(Array(12345)); 468 | ``` 469 | 470 | and the previous `array.Print(12345)` is now a syntax error. 471 | 472 | ##### 1.4.1.2 Member Initializer Lists 473 | Member initializer lists allow you to initialize member variables in the definition of a method. This turns out to provide 474 | some performance benefits for class-type member variables, since a call to the default constructor is avoided. For POD (plain old data) 475 | like ints and floats, though, it is the same as initializing them in the body of the method. 476 | 477 | ```c++ 478 | class Car { 479 | private: 480 | int year; 481 | int miles; 482 | std::string make; 483 | 484 | public: 485 | Car(const int year, const int miles, const std::string & make) : year(year), miles(miles), make(make) {} 486 | }; 487 | ``` 488 | 489 | Using the initializer list is basically the same as the following more verbose constructor implementation, notwithstanding the note above regarding performance: 490 | 491 | ```c++ 492 | Car(const int year, const int miles, const std::string & make) { 493 | this->year = year; 494 | this->miles = miles; 495 | this->make = make; 496 | } 497 | ``` 498 | 499 | Since C++11 initializer lists have some added functionality and curly braces `{}` can be used instead of parentheses `()` in the 500 | initializer list, but to maintain compatibility with older compilers you may want to use parentheses. The same applies in general 501 | to initialization syntax when creating objects. Many people prefer braces, and in some cases it's necessary (e.g. vector containing [100, 1] or a vector of one hundred 1s?), but to support older compilers you may consider using parentheses. 502 | 503 | #### 1.4.2 `new` and `delete` 504 | The `new` and `delete` operators (and their array counterparts, `new[]` and `delete[]`) are operators used to dynamically allocate 505 | memory for objects, much like C's `malloc()` and `free()`. 506 | 507 | More on these operators can be found [here](https://www.geeksforgeeks.org/new-and-delete-operators-in-cpp-for-dynamic-memory/). 508 | 509 | When manually allocating memory dynamically, it is the responsibility of the programmer to manage the memory and properly 510 | delete objects that have been allocated. 511 | 512 | #### 1.4.3 Copy Constructor and Copy Assignment 513 | Copy constructors and copy assigment operators allow one object to be constructed or assigned a copy of another object directly: 514 | ```c++ 515 | Foo a(10); 516 | Foo b(a); // (1): Copy via constructor 517 | Foo c ; 518 | c = a; // (2): Copy via assignment operator 519 | ``` 520 | 521 | This is accomplished by supplying a copy constructor and an assigment operator overload, both of which have a special syntax 522 | where they accept a const reference to an object of their same type. 523 | 524 | ```c++ 525 | class Foo { 526 | private: 527 | int data; 528 | 529 | public: 530 | // Default (no argument) constructor 531 | Foo() : data(0) {} 532 | 533 | // Single argument constructor 534 | explicit Foo(const int v) : data(v) {} 535 | 536 | // Copy constructor 537 | Foo(const Foo & f) : data(f.data) {} 538 | 539 | // Copy assignment operator 540 | Foo & operator=(const Foo & f) { 541 | data = f.data; 542 | return *this; 543 | } 544 | }; 545 | ``` 546 | 547 | Note that the compiler will always provide a default constructor, a default copy constructor, and a default copy assignment operator, so for simple cases (like this trivial example) you will not have to implement them yourself. More info on this can be found [here](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three). 548 | 549 | #### 1.4.4 Move Constructor and Move Assignment 550 | Sometimes instead of performing a copy you instead wish to completely move data from one object to another. This requires the use 551 | of a move constructor and move assignement operator. 552 | 553 | ```c++ 554 | class Movable { 555 | private: 556 | Foo * data_ptr; 557 | 558 | public: 559 | Movable(Foo data) : data_ptr(new Foo(data)) {} 560 | 561 | // Move constructor 562 | Movable(Movable && m) { 563 | // Point to the other object's data 564 | data_ptr = m.data_ptr; 565 | 566 | // Remove the other object's data pointer by 567 | // setting it to nullptr 568 | m.data_ptr = nullptr; 569 | } 570 | 571 | // Move assignment operator 572 | Movable & operator=(Movable && m) { 573 | data_ptr = m.data_ptr; 574 | m.data_ptr = nullptr; 575 | return *this; 576 | } 577 | 578 | ~Movable() { 579 | delete data_ptr; 580 | } 581 | }; 582 | ``` 583 | 584 | The move constructor and assignment operator can be used as follows: 585 | 586 | ```c++ 587 | Movable Bar() { 588 | // ... 589 | } 590 | 591 | int main() { 592 | Movable a(Bar()); // Using the move constructor 593 | Movable b = Bar(); // Using the move assignment operator 594 | } 595 | ``` 596 | 597 | Since `Bar()` creates an object that won't be used elsewhere and is deleted after the call, we can use the move constructor or 598 | move assignment operator to move the data to our object. 599 | 600 | A programming idiom called ['copy and swap'](https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom) makes use of the move constructor and can be a useful idiom. 601 | 602 | ### 1.5 Operator Overloading 603 | Operators such as `+`, `-`, `*`, etc. are familiar and ubiquitous when working with simple data types like integers and floating point 604 | numbers. These operators as well as others can also be overloaded to provide a clear syntactic meaning to your own classes. For example, 605 | when working with linear algebra you can overload the `+` operator to perform an element-wise addition of two vectors. Here's a brief 606 | example using complex numbers that allows you to use the `+` and `-` operators to easily add and subtract two complex numbers. 607 | 608 | There are two main ways to do operator overloading. The first is using normal member functions. The second uses the `friend` keyword and non-member methods that have access to the private member variables of the class. 609 | 610 | Using normal member functions (requires a getter method for the member variables): 611 | 612 | ```c++ 613 | // File: complex.h 614 | 615 | class Complex { 616 | private: 617 | double r = 0.0; // Real part, defaults to 0.0 618 | double i = 0.0; // Imaginary part, defaults to 0.0 619 | 620 | public: 621 | Complex(const double r, const double i) : r(r), i(i) {} 622 | 623 | // Accessor methods 624 | double GetReal(void) const { return r; } 625 | double GetImaginary(void) const { return i; } 626 | 627 | // + Operator 628 | Complex operator+(const Complex & a, const Complex & b) { 629 | return Complex(a.GetReal() + b.GetReal(), a.GetImaginary() + b.GetImaginary()); 630 | } 631 | 632 | // - Operator 633 | Complex operator-(const Complex& a, const Complex& b) { 634 | return Complex(a.GetReal() - b.GetReal(), a.GetImaginary() - b.GetImaginary()); 635 | } 636 | }; 637 | ``` 638 | 639 | Using `friend` methods: 640 | 641 | ```c++ 642 | // File: complex.h 643 | 644 | class Complex { 645 | private: 646 | double r = 0.0; // Real part, defaults to 0.0 647 | double i = 0.0; // Imaginary part, defaults to 0.0 648 | 649 | public: 650 | Complex(const double r, const double i) : r(r), i(i) {} 651 | 652 | // + Operator (declaration only) 653 | friend Complex operator+(const Complex & a, const Complex & b); 654 | 655 | // - Operator (declaration only) 656 | friend Complex operator-(const Complex& a, const Complex& b); 657 | }; 658 | 659 | // These are NOT member functions 660 | // They can also be defined inside the class body but leaving them outside 661 | // is a clearer reminder that they are not part of the class 662 | Complex operator+(const Complex & a, const Complex & b) { 663 | return Complex(a.r + b.r, a.i + b.i); 664 | } 665 | 666 | Complex operator-(const Complex& a, const Complex& b) { 667 | return Complex(a.r - b.r, a.i - b.i); 668 | } 669 | ``` 670 | 671 | In either case, the new operators can be used as follows: 672 | 673 | ```c++ 674 | int main() { 675 | Complex a(1, 2); // 1 + 2i 676 | Complex b(5, 3); // 5 + 3i 677 | 678 | Complex c = a + b; // 6 + 5i 679 | Complex d = a - b; // -4 - 1i 680 | } 681 | ``` 682 | 683 | It's also often useful to overload the output stream operator to provide a custom output string displaying the object's 684 | internal state in a human-readable format. This is done by overloading the `<<` operator and requires using the `` 685 | functionality. 686 | 687 | ```c++ 688 | #include 689 | 690 | class Complex { 691 | private: 692 | // ... 693 | public: 694 | // ... 695 | 696 | friend std::ostream & operator<<(std::ostream & os, const Complex & c); 697 | }; 698 | 699 | // Definition 700 | // Again, this is NOT a member function! 701 | std::ostream & operator<<(std::ostream & os, const Complex & c) { 702 | os << c.r << " + " << c.i << "i"; 703 | return os; 704 | } 705 | 706 | int main() { 707 | Complex a {1, 2}; 708 | Complex b {5, 3}; 709 | 710 | std::cout << a; // Prints: 1 + 2i 711 | std::cout << a + b; // Prints: 6 + 5i 712 | } 713 | ``` 714 | 715 | You can also similiarly overload the input stream operator (`>>`), and can read more about the various operators [here](http://en.cppreference.com/w/cpp/language/operators). 716 | 717 | ### 1.6 Templates 718 | Templates are a very powerful abstraction allowing you to generate compile-time methods/classes/etc. for any number of types while 719 | writing only one implementation. 720 | 721 | Say you have a method that adds two floating point number together, and another to add two integers together: 722 | 723 | ```c++ 724 | double Add(const double a, const double b) { 725 | return a + b; 726 | } 727 | 728 | int Add(const int a, const int b) { 729 | return a + b; 730 | } 731 | ``` 732 | 733 | That's great, but since both floating point numbers and integers implement the `+` operator you can use a template to instead 734 | write one generic implementation of a method that can operate on doubles, ints, floats, and (in this case) any other type that 735 | implements the `+` operator. 736 | 737 | A simple templatized version of `Add` would look something like this: 738 | 739 | ```c++ 740 | template // T becomes whatever type is used at compile-time 741 | T Add(const T & a, const T & b) { 742 | return a + b; // The type T must support the + operator 743 | } 744 | 745 | // Usages 746 | int main() { 747 | Add(3, 5); // int version 748 | Add(3.2, 5.8); // double 749 | Add(3.45f, 5.0f); // implicit float version: we leave off the here, since it can deduce the type from the context 750 | 751 | Complex a {1, 2}; // Custom class 752 | Complex b {5, 3}; 753 | Add(a, b); // Works because we added support for the + operator! 754 | } 755 | ``` 756 | 757 | In this simple example the compiler would generate four different methods, one for each type. Templating allows you to write more 758 | concise and modular code at the expense of generating a larger executable (code bloat). 759 | 760 | Templates are especially useful to create class templates. Class templates must be completely defined in a single header file. 761 | 762 | ```c++ 763 | // File: storage.h 764 | 765 | template // <--- 'class' is synonymous with 'typename' 766 | class Container { 767 | private: 768 | T data; 769 | public: 770 | explicit Container(const T & d) : data(d) {} 771 | }; 772 | 773 | // Usage 774 | int main() { 775 | Container a(1); 776 | Container b(10.0f); 777 | Container> c(a); 778 | } 779 | ``` 780 | 781 | > NOTE: More coming soon on templates... 782 | 783 | Read more about templates [here](https://www.geeksforgeeks.org/templates-cpp/) and [here](http://en.cppreference.com/w/cpp/language/templates). 784 | 785 | ## 2.0 General C++ Syntax 786 | ### 2.1 Namespaces 787 | In a large production project you may have thousands of symbols for various types, variables, methods, and so on. To avoid symbol names conflicting 788 | with one another you can use namespaces to logically separate symbol names in to broad categories. Namespaces are an inherent feature of C++; when you 789 | create a class and refer to a method as `ClassName::Method()` you are essentially using a namespace feature intrinsic to classes. 790 | 791 | For a brief namespace example, suppose that you have two data structures, both of which implement a `Node` class. In the following code, namespaces 792 | are used to allow the compiler (and the programmer) to distinguish between the two types. 793 | 794 | ```c++ 795 | // File: list.h 796 | 797 | namespace list { 798 | 799 | template 800 | struct Node { 801 | Node * next; 802 | Node * prev; 803 | T data; 804 | }; 805 | 806 | }; // namespace 807 | ``` 808 | 809 | ```c++ 810 | // File: bst.h 811 | 812 | namespace bst { 813 | 814 | template 815 | struct Node { 816 | Node * left; 817 | Node * right; 818 | T data; 819 | }; 820 | 821 | }; // namespace 822 | ``` 823 | 824 | ```c++ 825 | // File: main.cpp 826 | #include "list.h" 827 | #include "bst.h" 828 | 829 | int main() { 830 | list::Node a; 831 | bst::Node b; 832 | }; 833 | ``` 834 | 835 | The standard C++ library uses the namespace `std`, e.g. `std::cout`, `std::string`, `std::endl`, etc. While you can use a `using namespace foo;` directive to address 836 | symbols directly in the `foo` namespace without prefixing the `foo::` qualifier, this is generally considered bad practice as it pollutes the global namespace and 837 | sort of undermines the point of using namespaces in the first place. 838 | 839 | ```c++ 840 | #include 841 | using namespace std; 842 | 843 | cout << "Hello, World" << endl; // <--- BAD: pollutes the global namespace 844 | ``` 845 | 846 | ```c++ 847 | #include 848 | 849 | std::cout << "Hello, World" << std::endl; // <--- GOOD: It's clear that you're using symbols from the standard namespace 850 | ``` 851 | 852 | ### 2.2 References and Pointers 853 | Those familiar with C will be very intimately acquainted with pointers. C++ adds the concept of references, which is a powerful way to have *some* of the features of 854 | pointers while avoiding some of the pitfalls. Later versions of C++ also add [smart pointers](https://docs.microsoft.com/en-us/cpp/cpp/smart-pointers-modern-cpp?view=vs-2019), 855 | which allow for better memory management and scoping via `std::unique_ptr`, `std::shared_ptr`, and `std::weak_ptr`, as compared to traditional raw pointers. 856 | 857 | Raw pointers in C++ behave exactly the same way as they do in C: a pointer variable stores the address of whatever it is pointing to. You can think of pointers as 858 | essentially storing a link to another piece of data. You can access the data that the pointer points to with the `->` operator, or dereference it with the `*` operator. 859 | 860 | References are more akin to an alias. References cannot be `NULL` or `nullptr`, and references cannot be reassigned to reference something else after they have been created. 861 | Additionally, references do not take up extra memory; they share the same address as whatever they reference to. References cannot have multiple levels of indirection (pointers can), 862 | and there is no reference arithmetic like there is for pointers. You can access the underlying data of a reference directly by using the reference itself: that is, if it's a reference 863 | to an integer it can be used as an integer. If it's a reference to a class you can access the class members directly with the `.` operator. 864 | 865 | Although pointers are incredibly powerful, references are generally much safer, especially when passing objects to methods using pass-by-reference. It is very common in 866 | C++ code to pass an object as a `const` reference (if the data should be unmutable within the method) or a non-const reference rather than a raw pointer as is required in C. 867 | 868 | More on [references vs pointers here](https://stackoverflow.com/a/57492). 869 | 870 | In the following code, assume a 32-bit system, in which case the size of a pointer variable is 4 bytes, and that the stack grows towards higher memory addresses. 871 | 872 | ```c++ 873 | // Pointers 874 | int a = 10; // Ends up at memory address '0x2A000084', for example 875 | int b = 20; // Ends up at memory address '0x2A000088' 876 | 877 | int * ptr = nullptr; // ptr is a separate variable whose type is 'pointer to int' and whose value has been initialized to '0x00000000' 878 | printf("ptr = %p\n"); // Prints: 0x0 879 | 880 | ptr = &a; // The value of ptr is now the address of the variable 'a' 881 | std::cout << ptr << std::endl; // Prints: 0x2a000084 882 | std::cout << *ptr << std::endl; // Prints: 10 883 | 884 | ptr = &b; // The value of ptr is now the address of the variable 'b' 885 | std::cout << ptr << std::endl; // Prints: 0x2a000088 886 | std::cout << *ptr << std::endl; // Prints: 20 887 | ``` 888 | 889 | ```c++ 890 | // References 891 | int a = 10; // Ends up at memory address '0x2A000084', for example 892 | int b = 20; // Ends up at memory address '0x2A000088' 893 | 894 | int & ref_a = a; // ref_a is an alias of (reference to) the variable a 895 | int & ref_b = b; // ref_b is an alias of (reference to) the variable b 896 | 897 | std::cout << ref_a << std::endl; // Prints: 10 898 | std::cout << ref_b << std::endl; // Prints: 20 899 | 900 | std::cout << &ref_a << std::endl; // Prints: 0x2a000084 901 | std::cout << &ref_b << std::endl; // Prints: 0x2a000088 902 | 903 | ref_a = b; // SETS THE VALUE OF 'a' TO THE VALUE OF 'b'! 904 | 905 | std::cout << ref_a << std::endl; // Prints: 20 906 | std::cout << a << std::endl; // ALSO PRINTS: 20 ! 907 | 908 | int & ref_c; // ERROR! References must be initialized at their declaration 909 | ``` 910 | 911 | Perhaps the most widely used aspect of references is to pass objects by reference (sometimes constant reference) to a method. To avoid hammering the stack with 912 | large objects when you pass them by value it is nearly always preferrable to pass by reference, which is the term used when using either a reference *or* a pointer. 913 | Using a reference allows you to pass any size object by reference, while still allowing you to access the object directly. 914 | 915 | ```c++ 916 | // Pass by reference using a const reference 917 | void Foo(const Bar & bar) { 918 | int a = bar.GetValue(); 919 | 920 | if (bar.SomeMethod()) { 921 | // ... 922 | } 923 | 924 | bar.SetValue(10); // ERROR! Cannot modify a const reference! 925 | } 926 | 927 | // Pass by reference using a non-const reference 928 | void Foo(Bar & bar) { 929 | int a = bar.GetValue(); 930 | 931 | if (bar.SomeMethod()) { 932 | // ... 933 | } 934 | 935 | bar.SetValue(10); // Modifies 'bar' and thus whatever 'bar' references 936 | } 937 | ``` 938 | 939 | By passing an object by reference using a reference instead of a pointer you: 940 | * Don't need to check for `NULL` or `nullptr` since references cannot be null 941 | * Can access the referenced object's data directly instead of using the `->` operator or dereferencing a pointer 942 | * Make it clearer which parameters are meant to be *input* parameters and which are meant to be *output* parameters by using 943 | `const` to denote strictly input parameters 944 | * Gain the benefits of both passing by value and passing by reference since you don't need to use a lot of memory on the stack for your object 945 | 946 | Thus, passing by reference using a `const` reference is essentially the same as passing by value, but you avoid copying the object onto the stack. Passing by reference 947 | using a non-const reference is essentially the same as passing by reference using a pointer, but you are guaranteed that it is not null and it's as if the pointer 948 | is effectively dereferenced. 949 | 950 | ### 2.3 Keywords 951 | [Reference](http://en.cppreference.com/w/cpp/keyword) 952 | 953 | #### 2.3.1 General Keywords 954 | [`asm`](http://en.cppreference.com/w/cpp/language/asm) 955 | [`auto`](http://en.cppreference.com/w/cpp/language/auto) 956 | [`const`](http://en.cppreference.com/w/cpp/language/cv) 957 | [`constexpr` (*since C++11*)](http://en.cppreference.com/w/cpp/language/constexpr) 958 | [`explicit`](http://en.cppreference.com/w/cpp/language/explicit) 959 | [`export` (*until C++11*)](http://en.cppreference.com/w/cpp/keyword/export) 960 | [`extern` (*language linkage*)](http://en.cppreference.com/w/cpp/language/language_linkage) 961 | [`friend`](http://en.cppreference.com/w/cpp/language/friend) 962 | [`inline`](http://en.cppreference.com/w/cpp/language/inline) 963 | [`mutable`](http://en.cppreference.com/w/cpp/language/cv) 964 | [`noexcept` (*operator*)](http://en.cppreference.com/w/cpp/language/noexcept) 965 | [`noexcept` (*function specifier*)](http://en.cppreference.com/w/cpp/language/noexcept_spec) 966 | [`nullptr`](http://en.cppreference.com/w/cpp/language/nullptr) 967 | [`override`](http://en.cppreference.com/w/cpp/language/override) 968 | [`static` (*class member specifier*)](http://en.cppreference.com/w/cpp/language/static) 969 | [`template`](http://en.cppreference.com/w/cpp/language/templates) 970 | [`this`](http://en.cppreference.com/w/cpp/language/this) 971 | [`virtual` (*function specifier*)](http://en.cppreference.com/w/cpp/language/virtual) 972 | [`virtual` (*base class specifier*)](http://en.cppreference.com/w/cpp/language/derived_class) 973 | [`volatile`](http://en.cppreference.com/w/cpp/language/cv) 974 | 975 | #### 2.3.2 Storage Class Specifiers 976 | [Reference](http://en.cppreference.com/w/cpp/language/storage_duration) 977 | * `auto` (*until C++11*) 978 | * `register` (*until C++17*) 979 | * `static` 980 | * `extern` 981 | * `thread_local` (*since C++11*) 982 | 983 | #### 2.3.3 `const` and `dynamic` Cast Conversion 984 | * [`const_cast`](http://en.cppreference.com/w/cpp/language/const_cast) 985 | * [`dynamic_cast`](http://en.cppreference.com/w/cpp/language/dynamic_cast) 986 | 987 | ### 2.4 Preprocessor Tokens 988 | * `#if`: Preprocessor version of `if(...)` 989 | * `#elif`: Preprocessor version of `else if(...)` 990 | * `#else`: Preprocessor version of `else` 991 | * `#endif`: Used to end an `#if`, `#ifdef`, or `#ifndef` 992 | * `defined()`: Returns true if the macro is defined 993 | * `#ifdef`: Same as `#if defined(...)` 994 | * `#ifndef`: Same as `#if !defined(...)` 995 | * `#define`: Defines a text macro. See [here](http://en.cppreference.com/w/cpp/preprocessor/replace) for full explanation, including macro functions and predefined macros. 996 | * `#undef`: Un-defines a text macro 997 | * `#include`: Includes a source file 998 | * `#line`: Changes the current file name and line number in the preprocessor 999 | * `#error`: Prints an error message and stops compilation 1000 | * `#pragma`: Non-standard, can be used instead of header guards (`#ifndef HEADER_H` ...) 1001 | 1002 | ### 2.5 Strings (`std::string`) 1003 | [Reference](http://en.cppreference.com/w/cpp/string/basic_string) 1004 | 1005 | ### 2.6 Iterators (`std::iterator<...>`) 1006 | [Reference](http://en.cppreference.com/w/cpp/concept/Iterator) 1007 | 1008 | ### 2.7 Exceptions 1009 | [Reference](http://en.cppreference.com/w/cpp/error/exception) 1010 | 1011 | ### 2.8 Lambdas 1012 | [Reference](https://en.cppreference.com/w/cpp/language/lambda) 1013 | --------------------------------------------------------------------------------