├── .DS_Store ├── Images ├── .DS_Store ├── ArithmeticOperatorPrecedence.png ├── BST_iterator.png ├── LogicalOperatorsPrecedence.png ├── StackFunctionCall.png ├── bst_inorder_traversal.png ├── degree.jpg ├── graph.png ├── graphcycle.jpg ├── graphlist.png ├── graphmatrix.png ├── graphrep.png ├── move_summary.png ├── strategy_pattern.png └── strategy_pattern2.png ├── Lessons ├── Week 01 │ ├── 01howdy.md │ ├── 02objects_types_values.md │ ├── 03type_conversion.md │ └── 04expressionsOperators_statements.md ├── Week 02 │ ├── 05functions.md │ ├── 06compound_data.md │ ├── 07cmd_arguments.md │ ├── 08read_write_cmd_and_files.md │ ├── 09struct.md │ ├── 10git.md │ └── 11compilationExecution.md ├── Week 03 │ ├── 12make.md │ ├── 13errors.md │ ├── 14exceptions.md │ └── 15assert.md ├── Week 04 │ ├── 16pointers.md │ ├── 17arrays.md │ ├── 18references.md │ ├── 19function_overloading.md │ └── 20language_agnostic_intro_to_userdefined_types.md ├── Week 05 │ ├── 21classes.md │ ├── 22more_classes.md │ ├── 23operator_overloading.md │ └── 24streams.md ├── Week 06 │ ├── 25data_representation.md │ └── 26data_representation_cont.md ├── Week 07 │ ├── 27functions_and_stack.md │ ├── 28variables_on_the_stack.md │ ├── 29dynamic_allocation_on_free_store.md │ ├── 30implicit_free_store_contract.md │ ├── 31freestore_2darrays.md │ └── 32memory_testing_debugging_tools.md ├── Week 08 │ ├── 33RAII_dynamic_memory_in_classes.md │ ├── 34copy_semantics.md │ ├── 35dynamic_structures.md │ └── 36singly_linked_lists.md ├── Week 09 │ ├── 37function_templates.md │ ├── 38class_templates.md │ ├── 39inheritance.md │ └── 40inclusion_polymorphism.md ├── Week 10 │ ├── 41trees.md │ ├── 42bst.md │ ├── 43bst_queries.md │ └── 44bst_construction_insertion_destruction.md ├── Week 11 │ ├── 45graphs.md │ ├── 46more_graphs.md │ ├── 47implementing_undirected_graph_using_adjacency_lists.md │ └── 48DFS.md ├── Week 12 │ ├── 49move_semantics.md │ ├── 50smart_pointers.md │ ├── 51unique_pointers.md │ └── 52shared_pointers.md ├── Week 14 │ ├── 53design_patterns.md │ ├── 54iterators.md │ ├── 55doubly_linked_list_iterator.md │ └── 56binary_tree_iterator.md ├── Week 15 │ ├── 57programming_paradigms.md │ ├── 58functional_style_programming.md │ ├── 59function_pointers_and_function_objects.md │ └── 60lambda_functions.md └── compiler.md └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/.DS_Store -------------------------------------------------------------------------------- /Images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/Images/.DS_Store -------------------------------------------------------------------------------- /Images/ArithmeticOperatorPrecedence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/Images/ArithmeticOperatorPrecedence.png -------------------------------------------------------------------------------- /Images/BST_iterator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/Images/BST_iterator.png -------------------------------------------------------------------------------- /Images/LogicalOperatorsPrecedence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/Images/LogicalOperatorsPrecedence.png -------------------------------------------------------------------------------- /Images/StackFunctionCall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/Images/StackFunctionCall.png -------------------------------------------------------------------------------- /Images/bst_inorder_traversal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/Images/bst_inorder_traversal.png -------------------------------------------------------------------------------- /Images/degree.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/Images/degree.jpg -------------------------------------------------------------------------------- /Images/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/Images/graph.png -------------------------------------------------------------------------------- /Images/graphcycle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/Images/graphcycle.jpg -------------------------------------------------------------------------------- /Images/graphlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/Images/graphlist.png -------------------------------------------------------------------------------- /Images/graphmatrix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/Images/graphmatrix.png -------------------------------------------------------------------------------- /Images/graphrep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/Images/graphrep.png -------------------------------------------------------------------------------- /Images/move_summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/Images/move_summary.png -------------------------------------------------------------------------------- /Images/strategy_pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/Images/strategy_pattern.png -------------------------------------------------------------------------------- /Images/strategy_pattern2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasyaneti/CS128-Notes/1600e7c0fc6a84dc48ccd9a14c8093e7774467f2/Images/strategy_pattern2.png -------------------------------------------------------------------------------- /Lessons/Week 01/01howdy.md: -------------------------------------------------------------------------------- 1 | # 01/18/21: Howdy, World! 2 | 3 | ## The Writing Process 4 | - **Analysis**: requirements, figure out what should be done 5 | - **Design**: determine overall system structure 6 | - **Implementation**: writing code, debugging, and testing 7 | - **Repeat**: development process requires iterations of improving solutions 8 | 9 | ## Language Syntax and Semantics 10 | - Syntax is structure, semantics is meaning 11 | - It is possible to have syntactically correct statements, that are semantically wrong, ex: the statement is not doing what is intended or the statement causes the program to crash/produce an incorrect answer/behave incorrectly 12 | 13 | ## Classic First C++ Program 14 | ``` cpp 15 | #include 16 | 17 | int main() { 18 | std::cout << "Hello, World!" << std::endl; 19 | return 0; 20 | } 21 | ``` 22 | 23 | - Standard io is not a part of the core language, it must be included explicitly 24 | - All C++ applications must have a **main() function**, it is the entry point in the application 25 | - Returning zero here signifies successful termination 26 | - Braces { } denote a code block, the body of the function 27 | - std::cout is an output stream bound to the text terminal, any output the code generates is shown there 28 | - std::endl is also defined from iostream, it terminates the line 29 | - std tells the program that this entity is defined in the standard namespace 30 | - << is an insertion operator used to insert objects (in out case: "Hello World!") in output stream 31 | - In C++, "double qoutes" denote strings -------------------------------------------------------------------------------- /Lessons/Week 01/02objects_types_values.md: -------------------------------------------------------------------------------- 1 | # 01/19/22: Objects, types, and values 2 | 3 | ## Terminology 4 | 5 | | Term | Definition | 6 | | ---- | ---------- | 7 | | Type | Defines a set of possible values, along with a set of operations for that object | 8 | | Object | Abstraction of a computer memory cell | 9 | | Value | Bits in memory interpreted according to type | 10 | | Variable | Named object | 11 | | Declaration | Gives name to an object | 12 | | Definition | Declaration that sets memory aside for an object | 13 | 14 | ## Primitive (built-in) Types 15 | ### bool 16 | - Logical values 17 | - true or false 18 | - Booleans are converted into integers (true is 1 and false is 0), logic is applied on this converted value, a nonzero result is true vs. a zero result is false 19 | ### char 20 | - Characters 21 | - 'a', 'b', 'c' 22 | - Each character has a constant integer value 23 | - There are integral so logical and arithmetic operators can be applied to these 24 | - Can store 26 English alphas, digits 0-9, basic punctuation characters 25 | - Escape sequence characters use backslashs: '\n', '\t', '\0', etc. 26 | ### int 27 | - Integer value: short, int, long 28 | - 10, 29, 992492 29 | - ints come in **signed** (can hold all ints)/**unsigned** (CAN ONLY HOLD POSITIVE INTS!) form, signed is default for "plain" ints 30 | - Integer literals: decimal (base 10) 31 | - Prefix: 0b is binary (base 2), 0 is octal (base 8), 0x is hexademical (base 16) 32 | - Suffix: U for unassigned literals, L for long literals 33 | 34 | ### double 35 | - Floating-point values, computer's approximation of a real number 36 | - 4.5, 6.3, 9.2 37 | - The meaning of float (single-precision, suffix F) vs. double (double-precision) vs. long double (extended-precision, suffix L) are implementation defined 38 | 39 | ## Compound Types 40 | - Pointer type: int* 41 | - Array type: char[] 42 | - References: int& 43 | - Data structures and classes 44 | 45 | ## Variables 46 | - Variables have 6 attributes: name, type, address, scope, value, lifetime 47 | ### Name 48 | - Rules 49 | - First character must be a letter 50 | - C++ is case-sensitive 51 | - Underscore is allowed, but if name starts with an underscore, convention indicates that it is a language facility 52 | - Keywords cannot be used as names 53 | - Convention 54 | - Descriptiveness of a name should be proportional to the scope 55 | - Do not use abbreviations that others would not understand 56 | - Google C++ Style Guide 57 | - Names are all lowercase, underscores between words to represent spaces 58 | - In Classes, we use a trailing underscore at the end 59 | 60 | ### Address 61 | - Variable's location in machine memory 62 | - Alias: multiple names associated with the same address, any change to one alias changes all other aliases 63 | 64 | ### Value 65 | - The value is the contents of the memory cell associated with the variable 66 | 67 | ### Lifetime 68 | - Binding between an attribute and an entity 69 | - **Allocation** is the process of taking the memory cell that is associated with the variable from the pool or available memory 70 | - **Deallocation** is the process of replacing a memory cell back into the pool of available memory 71 | - The lifetime is the time during which the variable is bound to a specific memory location 72 | 73 | ### Scope 74 | - Part of the program where a variable name has meaning 75 | - Most scopes are enclosed in curly braces 76 | - Local (where variable is declared) vs. non-local (where variable is not declared) 77 | - Two variables can have the same name in different scopes 78 | - Nested scope allows to hide variables and have different uses for inner vs. outer scope 79 | - Variables can be declared anywhere, but it is good practice to define them closest to the first use and initialize immediately after declaration 80 | 81 | ## Declaration 82 | - Before the name, we must specific the type for the compiler 83 | - Many declarations are also definitions, all the built-in primitive types have default values 84 | 85 | ``` cpp 86 | const int i = 2; 87 | ``` 88 | 89 | | Specifier (optional) | Base type | Declarator | Initializer (optional) | 90 | | -------------------- | --------- | ---------- | ---------------------- | 91 | | const | int | i | = 2 | 92 | 93 | ### Declarator 94 | - Optional add-on for base types 95 | - Postfix binds tighter than prefix 96 | - These apply to individual names only 97 | 98 | | Declarator | What it is | Where | 99 | | ---------- | ---------- | ----- | 100 | | * | pointer | prefix | 101 | | *const | constant pointer | prefix | 102 | | & | reference | prefix | 103 | | [] | array | postfix | 104 | | () | function | postfix | 105 | 106 | ## Constants 107 | - Name starts with k, camel case capitalization 108 | ``` cpp 109 | const int kReadOnlyVariable = 7; 110 | ``` 111 | 112 | ## Initialization and Assignment 113 | Initialization 114 | - Before: box is empty 115 | - After: box holds a coin 116 | - Reinitialize: remove the first coin, put in new coin 117 | 118 | Good practice: declare and initialize at the same time 119 | ``` cpp 120 | int a = 5 121 | ``` 122 | Bad practice: declare and initialize seperately -> declare, initialized with random value, assignment 123 | ``` cpp 124 | int a; // a has been allocated space 125 | a = 5; 126 | ``` -------------------------------------------------------------------------------- /Lessons/Week 01/03type_conversion.md: -------------------------------------------------------------------------------- 1 | # 01/20/22: Type Conversions 2 | 3 | ## Narrowing Conversion 4 | - Value is converted to type that cannot store to the original precision 5 | - Not safe because value can be changed in conversion process 6 | - Ex: double to float, because double is "larger" than float 7 | 8 | ## Widening Conversion 9 | - Value is converted to type that can store values of more precision than the orignal 10 | - Ex: int to double 11 | 12 | ## Implicit Type Conversion 13 | - Automatic conversion of values from one type to another = coercion 14 | - Narrow type to wide type 15 | - For example, mixed-type expressions: 3.1 + 5 = 3.1 + 5.0 = 8.1 (result is double) 16 | 17 | ## Explicit Type Conversion 18 | - C++ requires casting to perform this type of conversion 19 | ``` cpp 20 | // static_cast(value_to_cast) 21 | 22 | static_cast(5); 23 | static_cast('a'); 24 | ``` 25 | 26 | ## Safe vs. Unsafe Type Conversions 27 | | Safe | Unsafe | 28 | | ---- | ------ | 29 | | bool to char | char to bool | 30 | | bool to int | int to bool | 31 | | bool to double | double to bool | 32 | | char to int | int to char | 33 | | char to double | double to char | 34 | | int to double | double to int | -------------------------------------------------------------------------------- /Lessons/Week 01/04expressionsOperators_statements.md: -------------------------------------------------------------------------------- 1 | # 01/21/22: Expressions and Statements 2 | 3 | - Arithmetic operations are the same as basic math 4 | - The smallest piece of programming language that has meaning is called a token 5 | 6 | ## Basic Concepts 7 | Unary operator: -7 8 | Binary operator: 7 + 3 9 | Ternary operator: x = a ? b : c 10 | Both: 8 - 7 11 | 12 | ## Grouping Operators/Operands 13 | **Precedence** 14 | - Multiplication/division have higher precedence than addition/subtraction 15 | - Ex: 3 + 4 * 5 = 23 16 | 17 | **Associativity** 18 | - Arithmetic operators are **left to right** associative, ex: 20 - 15 - 3 = 2 19 | ![Arithmetic Operator Precedence](/Images/ArithmeticOperatorPrecedence.png) 20 | 21 | - Assignment operators are **right to left** associative 22 | ``` cpp 23 | int i = 0 24 | int j = 1 25 | i = j = 5; // i gets 5, not 1 26 | ``` 27 | ![Logical Operator Precedence](/Images/LogicalOperatorsPrecedence.png) 28 | 29 | **Order of Evaluation** 30 | - Unspecified, it changes based on the implementation 31 | ``` cpp 32 | int i = f1() * f2(); 33 | // under the assumption that f1 and f2 are manipulating the same data 34 | ``` 35 | 36 | ## Statements 37 | Simple statements in C++ end with a semicolon 38 | ``` cpp 39 | int n = 3; 40 | ``` 41 | The most simple statement is an empty statement 42 | ``` cpp 43 | ; 44 | ``` 45 | 46 | Compound statements, AKA blocks, are bound by curly braces and not terminated by semicolon 47 | 48 | ## Branching: if/else 49 | If statements are executed if conditional is true 50 | ``` cpp 51 | std::cout << "Freeze warning should "; 52 | if (temperature <= 32) 53 | std::cout << "be issued." << std::endl; 54 | else 55 | std::cout << "not be issued." << std::endl; 56 | ``` 57 | 58 | The else branch need not be specified if the program is modified like so: 59 | ``` cpp 60 | std::cout << "Freeze warning should "; 61 | if (temperature > 32) 62 | std::cout << "not" << std::endl; 63 | std::cout << " be issued." << std::endl; 64 | ``` 65 | 66 | Branches of if statements are scopes, i.e. variable created inside cannot be accessed from outside. 67 | 68 | Else if is a compound statement 69 | ``` cpp 70 | if (number % 15 == 0) { 71 | // statement 1 72 | } else if (number % 3 == 0) { 73 | // statement 2 74 | } else if (number % 5 == 0) { 75 | // statement 3 76 | } else { 77 | // statement 4 78 | } 79 | ``` 80 | 81 | Convention (Google C++ Style Guide) 82 | - Use curly braces for if, else if, else 83 | - Put one space between the following 84 | - if and the opening parenthesis 85 | - closing parenthesis and the curly brace 86 | - NO spaces between the parentheses and the condition 87 | - Break line after opening brace, all subsequence else/if statements must be on the same line with a space 88 | 89 | Principles of if 90 | - Write the common cases first, usual oines later 91 | - Encode complex boolean expressions in methods, naming methods the meaning of the expression 92 | 93 | ## Branching: switch 94 | - Allows selection based on comparison of a value against several constants 95 | - Each case is terminated by **break** 96 | ``` cpp 97 | switch (op_code) { 98 | case 0: { 99 | z = x - y; 100 | break; 101 | } 102 | case 1: // fall through because NO BREAK 103 | case 2: { 104 | z = x + y; 105 | break; 106 | } 107 | default: { 108 | assert(false); 109 | } 110 | } 111 | ``` 112 | 113 | Technicalities and Convention 114 | - We must switch on the value of an integer, char, or enumeration type 115 | - Case label must be known at compile time, i.e. no variable 116 | - Case label values must be unique 117 | - If there is no break, you should make a comment that fall through is intentional 118 | - Remember to end cases with break! 119 | - If conditional is not enumerated value, you should always have a **default case** 120 | 121 | ## Iteration: while loop 122 | - Repeatedly execute a statement as long as the condition is true 123 | - Condition must converge to smaller steps so the loop can terminate 124 | ``` cpp 125 | int x = 10; 126 | while (x > 1) { 127 | std::cout << x << std::endl; 128 | x = x - 1; 129 | } 130 | ``` 131 | 132 | ## Iteration: do-while loop 133 | - Similar to while, but it is guarunteed to execute at least once since the condition is checked at the end 134 | - std::cin takes input from keyboard 135 | ``` cpp 136 | int x = -1; 137 | do { 138 | std::cout << "Enter a positive number: "; 139 | std::cin >> x; 140 | } while (x <= 0); 141 | ``` 142 | 143 | ## Iteration: for loop 144 | - Similar to while, but the management of the conditional is at the top 145 | - This is easier to understand 146 | - Variables created in the initialization of the for loop can only be used inside the loop, increment the control variable only inside the header 147 | Header contains: 148 | - Initialization of the control variable 149 | - Continuation criterion 150 | - Step operation 151 | 152 | Program below prints squares of numbers 1 through 10, inclusive 153 | ``` cpp 154 | for (int i = 1; i <= 10; ++i) { 155 | std::cout << i << '\t' << pow(i, 2) << std::endl; 156 | } 157 | ``` 158 | 159 | ## Loop Control 160 | - **break** = terminates the loop entirely 161 | - **continue** = ends the current iteration and continues with the subsequent iteration 162 | -------------------------------------------------------------------------------- /Lessons/Week 02/05functions.md: -------------------------------------------------------------------------------- 1 | # 01/24/21: Introduction to Functions 2 | 3 | ## Writing Functions Well (clean code) 4 | Functions are important because they organize any program by compartmentalizing behavior. Functions should have the following: 5 | - Intuitive and consistent names 6 | - One level of abstraction 7 | - Not longer than 40 lines 8 | - Ideally 0-1 arguments, no more than 3! 9 | - Use if/else statements to call functions 10 | - Top-down narrative 11 | 12 | 13 | ## Functions (based on # of arguments) 14 | ### Monadic Functions 15 | Reasons for writing functions with 1 argument: 16 | - Ask a question about the object 17 | - Perform an operation on an object, returned transformed object 18 | 19 | Function name should be in the verb-noun pair form. 20 | 21 | ### Dyadic Functions 22 | These are sensible when parameters have a natural ordering. Still, convert to monadic when possible. These function names should be desriptive and tell you what information you need to provide for both arguments, i.e. programmer shouldn't have to take a second look at the function signature 23 | 24 | ### Triadic Functions 25 | Use only after careful consideration, ex: 26 | ``` cpp 27 | // triad 28 | Circle MakeCircle(int x, int y, int radius) { } ; 29 | 30 | // dyadic 31 | Circle MakeCircle(int center, int radius) { } ; 32 | ``` 33 | 34 | ## More Notes 35 | - Functions should only do one thing, it should be predictable behavior 36 | - All of the things we need to know to call a function should be there in the first line 37 | - Do not pass booleans! 38 | - Repeated code increases risk for error 39 | - Refactoring code into functions helps compartmentalize behavior 40 | - Line breaks in functions indicates seperate thoughts, use them carefully! 41 | 42 | ## C++ Syntax 43 | - base type (return type) 44 | - identifier (name) 45 | - list of parameters seperated by commas 46 | - function body 47 | 48 | ## C++ Functions 49 | Function declaration 50 | ``` cpp 51 | int Square(int x); 52 | ``` 53 | Function definition 54 | ``` cpp 55 | int Square(int x) { 56 | return x*x; 57 | } 58 | ``` 59 | In our case, we will do the declaration of the function in the header file (.h), define the function in a different file (.cc), and include the file in our main file. **This way, we never need to see the C++ files, the header file tells us all the functions avalible to us and what informatin they use/return!** 60 | 61 | ### my_math_ops.h 62 | ``` cpp 63 | #ifndef MY_MATH_OPS_H 64 | #define MY_MATH_OPS_H 65 | 66 | int Square(int x); 67 | 68 | #endif 69 | ``` 70 | 71 | ### main.cc 72 | ``` cpp 73 | #include 74 | #include "my_math_ops.h" 75 | 76 | int main() { 77 | int num = 0; 78 | // take user input 79 | std::cin >> num; 80 | // function call 81 | int num_squared = Sqaure(num); 82 | } 83 | ``` 84 | 85 | ### my_math_ops.cc 86 | ``` cpp 87 | #include "my_math_ops.h" 88 | 89 | int Square(int x) { 90 | return (x * x); 91 | } 92 | ``` 93 | 94 | ## What happens when function is called? 95 | 1. Function's parameters are initialized 96 | 2. Control is transferred to Square 97 | 3. Return value is transfered back to calling function 98 | 99 | ## HW Tip 100 | ``` cpp 101 | // Use division by 10 to chop int by 1 digit 102 | // Use modulo by 10 to retrieve last digit 103 | 104 | int n = 123; 105 | int lastDigit = 0; 106 | 107 | n = n / 10; 108 | lastDigit = n % 10; 109 | // NOW: n is 12, lastDigit is 2 110 | n = n / 10; 111 | lastDigit = n % 10; 112 | // NOW: n is 1, lastDigit is 1 113 | ``` -------------------------------------------------------------------------------- /Lessons/Week 02/06compound_data.md: -------------------------------------------------------------------------------- 1 | # 01/26/22: Compound Data 2 | 3 | ## std:vector 4 | - Vectors work similar to arrays, **sequence of elements** that can be accessed by **index** (starts with 0) 5 | - Vector stores elements and size 6 | - See all vector functions at the [API](https://en.cppreference.com/w/cpp/container/vector) 7 | 8 | 9 | ``` cpp 10 | #include 11 | 12 | // initialize with list 13 | std::vector v = {1, 2, 3, 5}; 14 | 15 | // declare with vector size 4 16 | std::vector v(4); 17 | 18 | // declare with vector size and specified default value 19 | std::vector v(4, 1.5); 20 | 21 | // access elements 22 | v[2]; 23 | v[2] = 10; 24 | 25 | // better way to access elements because it throws an obvious error when index greater than vector size 26 | v.at(2); 27 | v.at(2) = 20; 28 | ``` 29 | 30 | ### Traverse Vector 31 | ``` cpp 32 | // for loop 33 | for (size_t index = 0; i < v.size(); ++index) { 34 | std::cout << vect.at(index) << " "; 35 | } 36 | 37 | // enhanced for loop 38 | for (int val : v) { 39 | std::cout << val << " "; 40 | } 41 | ``` 42 | 43 | ### Vector push_back() 44 | ``` cpp 45 | std::vector vect; 46 | 47 | vect.push_back(10); 48 | // vect is now: [10] 49 | vect.push_back(1); 50 | // vect is now: [10, 1] 51 | ``` 52 | 53 | ## 2D Vector 54 | ``` cpp 55 | // each type within the bigger vector is an int vector 56 | std::vector> vect; 57 | std::vector a = {1}; 58 | std::vector b = {2, 3}; 59 | vect.push_back(a); 60 | vect.push_back(b); 61 | 62 | // access 2D vector 63 | vect.at(1).at(1); // = 3 64 | 65 | // declare 2D vector with size 66 | unsigned int height = 4; 67 | unsigned int width = 2; 68 | std::vector> = vect(height, std::vector)(width, 0); 69 | 70 | /* vect looks like 71 | |---|---| 72 | | 0 | 0 | 73 | |---|---| 74 | | 0 | 0 | 75 | |---|---| 76 | | 0 | 0 | 77 | |---|---| 78 | | 0 | 0 | 79 | |---|---| 80 | */ 81 | ``` 82 | 83 | ### Traverse 2D Vector 84 | ``` cpp 85 | for (size_t row = 0; row < vect.size(); ++row) { 86 | for (size_t col = 0; col < vect.at(row).size(); ++col) { 87 | std::cout << vect.at(row).at(col) << " "; 88 | } 89 | std::cout << std::endl; 90 | } 91 | ``` 92 | 93 | ## std::string 94 | - All functions avalible at [API](https://www.cplusplus.com/reference/string/string/) 95 | 96 | ``` cpp 97 | #include 98 | 99 | std::string full_name : "Lasya"; 100 | 101 | // concatenate 102 | full_name = full_name + " Neti"; 103 | 104 | // substring 105 | std:string first_name = full_name(0, 5); 106 | 107 | // char at 108 | char c = full_name.at(0); 109 | ``` 110 | 111 | ## std:map 112 | - key-value pairs 113 | - Keys must be unique 114 | - If key has no pairing, value is assigned default value 115 | - [API](https://www.cplusplus.com/reference/map/map/) 116 | 117 | ``` cpp 118 | #include 119 | 120 | std::map phone_book; 121 | phone_book["Bob"] = 123456789; 122 | 123 | // at 124 | phone_book.at("Lasya"); // returns ERROR 125 | 126 | // insert 127 | std::pair pair; 128 | pair.first = "Lasya"; 129 | pair.second = 123456789; 130 | phone_book.insert(pair); 131 | 132 | // insert returns a pair::second boolean -- true if new mapping inserted, false if key already existed 133 | // do not use these right now! 134 | auto result = phone_book.insert(std::pair("Lasya", 1234567890); 135 | if (result.second == false) { 136 | std::cout << "Value already exists" << std:endl; 137 | } 138 | ``` 139 | 140 | ### Iterating over map 141 | ``` cpp 142 | for (auto const& [key, value] : your_map) { 143 | std::cout << key << ":" << value << std::endl; 144 | } 145 | ``` -------------------------------------------------------------------------------- /Lessons/Week 02/07cmd_arguments.md: -------------------------------------------------------------------------------- 1 | # 01/27/21: Command Line Arguments 2 | 3 | 2 parameters can be passed to the main() function: 4 | - argc stores the argument count 5 | - argv stores the arguments in a vector, starting with argv[0] as the exec file commmand 6 | 7 | ``` cpp 8 | #include 9 | #include 10 | 11 | int main(int argc, char* argv[]) { 12 | // you can use strings passed to the program from the terminal when it is executed 13 | std::string arg = argv[0]; 14 | } 15 | ``` 16 | 17 | If bash command is used incorrectly, the output is a "usage" error along with the exec name. 18 | 19 | When you need to deal with command line arguments: 20 | - Check to see the number of arguments passed 21 | - C style strings need to be converted into std::strings 22 | -------------------------------------------------------------------------------- /Lessons/Week 02/08read_write_cmd_and_files.md: -------------------------------------------------------------------------------- 1 | # 01/27/22: Basic input/output 2 | 3 | - use #include !! 4 | 5 | ## Writing to standard out 6 | ``` cpp 7 | #include 8 | 9 | // insert objects/literals into the stream 10 | std::string name = "Lasya" 11 | int phone_number = 123456789; 12 | 13 | std::cout << "Name: " << name << " Phone Number: " << phone_number << std:endl; 14 | ``` 15 | 16 | **!!** Cannot insert **objects** or **vectors** directly into the stream, we need to iterate over it and insert every item individually **!!** 17 | 18 | ## Writing to files 19 | ``` cpp 20 | #include 21 | 22 | std::ofstream ofc{"filename.ext"}; 23 | // check if you can open the file 24 | if (!ofs.is_open()) { 25 | // error handling if you cannot open file 26 | } 27 | 28 | std::string name = "Lasya Neti"; 29 | int number = 11; 30 | 31 | // the contents of the file will look exactly like the output of this stream 32 | ofs << "Name: " << name << " Number: " << number << std:endl; 33 | ``` 34 | 35 | NOTE: binding a file stream to a file will **override** contents 36 | 37 | ### To append instead of overriding file content 38 | ``` cpp 39 | std::ofstream ofs {"filename.ext", std::ofstream::app;} 40 | ``` 41 | 42 | ## Reading from standard in 43 | std::cin reads by breaking around whitespace; line feeds count as whitespace 44 | 45 | ``` cpp 46 | #include 47 | 48 | // input will be entere4d via terminal 49 | 50 | // declare objects you want to get input for 51 | std::string first_name; 52 | std::string last_name; 53 | 54 | // ask user for input 55 | std::cout << "Enter your name: "; 56 | std::cin >> first_name >> last_name; 57 | 58 | // to read full line instead of spliting by whitespace 59 | std::getline(std::cin, full_name); 60 | 61 | // reading int, double, etc. heavily relies on whitespace 62 | int i = 0; 63 | double d = 0.0; 64 | std::string str; 65 | 66 | std::cin >> t >> d >> str; 67 | ``` 68 | 69 | ### Funky mixed input 70 | ``` cpp 71 | // input "3.14/n" 72 | int i = 0; 73 | double d = 0.0; 74 | 75 | std::cin >> i >> d; 76 | // i stores 3, d stores 0.14 77 | ``` 78 | 79 | ## Readings from files 80 | ``` cpp 81 | #include 82 | 83 | std:ifstream ifs{"filename.ext"}; 84 | if (!if.is_open()) { 85 | // error handling 86 | } 87 | int i = 0; 88 | double j = 0; 89 | std::string str; 90 | 91 | // readings from ifs, not cin 92 | ifs >> i >>> j >> str; 93 | ``` 94 | 95 | ## Vocab 96 | - ">>" is the extraction operator 97 | - "<<" is the insertin operation -------------------------------------------------------------------------------- /Lessons/Week 02/09struct.md: -------------------------------------------------------------------------------- 1 | # 01/27/22: struct 2 | 3 | ## struct 4 | - std::vector holds elemets of the SAME type 5 | - structs can hold arbitrary types 6 | 7 | ``` cpp 8 | struct Contact { 9 | std::string first_name; 10 | std::string last_name; 11 | long phone_number; 12 | std::string email; 13 | }; 14 | 15 | // this defines a new type called CONTACT consisting of items you can store as entry in say, a phone book! 16 | 17 | // use dot (.) notation to access elements 18 | Contact person; 19 | person.first_name = "Lasya"; 20 | person.last_name = "Neti"; 21 | person.phone_number = 123456789; 22 | person.email = "abc@illinois.edu"; 23 | 24 | // uniform initialization 25 | Contact person = {"Lasya", "Neti", 123456789, "abc@illinois.edu"};` 26 | ``` 27 | 28 | ## Shortcomings of structs (right now) 29 | - No mechanism for figuring out if two structs are equal (like the Java objects equals() function) 30 | - No mechanism to print contents of a struct (like Java object toString() function) 31 | 32 | ## Google C++ Style Guide for structs 33 | - struct type name must start with capital letter nad have a capital letter for each new word, no underscores 34 | - Data members are all lowercase with underscores between words 35 | - Use a struct **only** for passive objects that carry data 36 | - Use **struct** over **pair** or **tuple** since they are more structured, especially when elements can have meaningful names 37 | 38 | ### file: contact.h 39 | ``` cpp 40 | #ifndef CONTACT_H 41 | #define CONTACT_H 42 | 43 | #include 44 | 45 | struct Contact { 46 | std::string first_name; 47 | std::string last_name; 48 | long phone_number; 49 | std::string email; 50 | }; 51 | 52 | #endif 53 | ``` 54 | 55 | ### file: main.cc 56 | ``` cpp 57 | #include 58 | #include "contact.h" 59 | 60 | int main() { 61 | Contact person = {"Lasya", "Neti", 123456789, "abc@illinois.edu"}; 62 | } 63 | ``` -------------------------------------------------------------------------------- /Lessons/Week 02/10git.md: -------------------------------------------------------------------------------- 1 | # 01/28/22: Git 2 | 3 | - **git init** 4 | - creates empty repo in current directory 5 | - no commits or files tracked 6 | - **git clone {url}** 7 | - downloads external repo from github and sets it up as local repo in current directory 8 | - **git status** 9 | - shows the status of the files in current directory 10 | - **git add {file/directory}** 11 | - marks files to be committed 12 | - doesn't mean an automatically commit 13 | - "git add ." to add all files 14 | - "git add -u" to add files that you changes 15 | - **git commit -m "my message"** 16 | - commits added files to repo 17 | - attaches name/email when you commit 18 | - "git commit -am "commit all changed" (adds all files and commits in one command) 19 | - **git push** 20 | - uploads new commits to origin (github) 21 | 22 | ## Branches 23 | - diverge the linear history of git version control by splitting into branches 24 | - **checking out** is the process of creating a new branch and diverging from initial branch 25 | - changes can be made in this branch 26 | - if all goes well, these can be merged if no conflicts! 27 | - **merge conflicts** need to be resolved when same code is modified 28 | 29 | ### Git Commands 30 | - **git checkout** 31 | - "checks out" new branch 32 | - also used to create new branches 33 | - Usage: git checkout "existing-branch" 34 | - Usage: git checkout -b "add-new-branch" 35 | - all commits on this branch remain on this branch, and do not affect main! 36 | - **git merge** 37 | - without conflict 38 | - "merges" branch into current branch 39 | - Usage: git merge "other-branch" 40 | - **git merge** with merge conflicts 41 | - in vscode, you can either select choices or physically go in the code and delete the conflict parts 42 | - **pull requests** 43 | - used in bigger companies/repos 44 | - allows people to review code before merging 45 | - you can add labels, assign people, etc. to organize pull requests! 46 | - done on the github website -------------------------------------------------------------------------------- /Lessons/Week 02/11compilationExecution.md: -------------------------------------------------------------------------------- 1 | # 01/28/22: Compilation and Execution 2 | 3 | ## Compilation Pipeline (broken down further below) 4 | 1. HelloWorld.cpp and header files (.h) 5 | 2. **Preprocessor** (translation unit) -> temp file -> fed to compiler 6 | 3. Compiler takes source code -> generates **assembly file** (.s) 7 | 4. Assembler takes assembly files -> generates **object code** (.o) 8 | 5. HelloWorld.o and library object code -> goes through **linker** 9 | 6. Linker -> **executable** HelloWorld 10 | 11 | ## Preprocessor 12 | - "#ifndef XXX_H" checks if that header file is defined already, if yes the body doesn't run 13 | - if not, "#define XXX_H" is right below it and triggers the declaration 14 | - "endif" ends the header file declaration 15 | 16 | ## Compiler 17 | - Looks at variables to make sure everything is declared correctly 18 | - Type checking 19 | - **Assembly code** (.s) is the generated output 20 | 21 | ## Assembler 22 | - Converts assembly code into **object code** (.o), i.e the generated output 23 | - Object code is binary code 24 | 25 | ## Linker 26 | - All object code is aggregated at the end once each source file is processed = and fed to the linker 27 | - Every object code file must be mentioned specifically to be collected 28 | - **Executable** is the generated output -> use this to run main and get your output -------------------------------------------------------------------------------- /Lessons/Week 03/12make.md: -------------------------------------------------------------------------------- 1 | # 01/31/22: Build Systems (make) 2 | 3 | ## Make 4 | - "Makefile", case-sensitive 5 | - Building rool that describes how to get an output 6 | - Make files need to be intended with tabs 7 | - **See below**: output, input, recipe + steps 8 | - Blackslashes (\\) break it into different lines 9 | 10 | ``` Makefile 11 | bin/main.out: src/main.cc 12 | slang++ -stc++2- -stdlib=libc++ ... etc ... 13 | ``` 14 | 15 | ## Make - variables 16 | - set with "=" 17 | - evaluated with "$(_)" 18 | - ex: clang++ is stored in CXX, so you don't have to enter it everytime 19 | 20 | ``` Makefile 21 | CXX = clang++ 22 | 23 | bin/make.out: src/main.cc 24 | $(CXX) 25 | ``` 26 | 27 | ## Make - evaluating multiple rules 28 | Can update variables as it goes depending on which files have changes made to them 29 | 30 | ## "phony" targets 31 | - These run independently of the file system status 32 | - exec: run the compiled program with specific arguments 33 | - test: build and run unit tests 34 | - clean: remove any generated or cached files 35 | 36 | ## Automatic variables 37 | - $@: name of the rule's output 38 | - $<: the first dependency 39 | - $>: all dependencies 40 | - $: the directory of the output -------------------------------------------------------------------------------- /Lessons/Week 03/13errors.md: -------------------------------------------------------------------------------- 1 | # 02/02/22: Software and Errors 2 | 3 | ## Readable Code 4 | - Use meaningful names 5 | - Don't use names that are super close with tiny, missable differences 6 | - Use pronounceable names 7 | - Avoid single-letter variables 8 | - Pick one word per concept 9 | 10 | ## Compile Errors 11 | - Finds syntax (ex: missing semi colon) and type errors (ex: assigning literal to variable of different datatype) 12 | 13 | ## Link-Time Errors 14 | - Errors when linker is trying to combine object files into an executable program 15 | - Once names are defined, the linker must be able to find their definitions, references, etc. 16 | - Linker also alerts if you defined a function in more than one way 17 | 18 | ## Runtime Errors 19 | - Detected by computer: int division by 0 20 | ``` cpp 21 | int numerator = 1; 22 | int denominator = 0; 23 | int result = numerator / denominator; 24 | ``` 25 | 26 | - Detected by library: index out of range 27 | ``` cpp 28 | std::vector my_values {1, 2, 3}; 29 | for (int i = 0; i <= my_values.size(); i++) { 30 | std::cout << my_values.at(i) << std::endl; 31 | } 32 | ``` 33 | 34 | - Detected by user-code: we write our own code to error-handle 35 | ``` cpp 36 | int positive_val = 0; 37 | std::cin >> positive_value; 38 | if (positive_value <= 0) { 39 | return 1; 40 | } 41 | ``` 42 | 43 | ## Logic Errors 44 | - Also occur at runtime 45 | - When the code itself is not doing what is intended -------------------------------------------------------------------------------- /Lessons/Week 03/14exceptions.md: -------------------------------------------------------------------------------- 1 | # 02/03/22: Exceptions 2 | 3 | When an unexpected condition occurs, you can handle it by throwing an exception 4 | 5 | ## Try-Catch 6 | ### main() 7 | ``` cpp 8 | try { 9 | double area = AreaRectangle(-10, 10); 10 | } 11 | catch (const std::runtime_error& e) { 12 | // handle the error 13 | // set area to 0 by default when any and all errors thrown 14 | area = 0; 15 | } 16 | ``` 17 | 18 | ### actual cpp file 19 | ``` cpp 20 | double AreaRectangle(double width, double height) { 21 | double area = width * height; 22 | if (area < 0) { 23 | throw std::runtime_error("InvalidArea"); 24 | } 25 | } 26 | ``` 27 | 28 | ## Notes 29 | - NEVER use exceptions to handle local-errors, i.e. catching errors right there in the same file 30 | 31 | ## 32 | - You cannot "throw std::exception", but you can throw any children exceptions 33 | - **std::runtime_error(std::string)** is thrown during runtime 34 | - **std::logic_error(std::string)** is an exception thrown during runtime for logical errors 35 | - **std::invalid_argument** is thrown to alert the user that the input is invalid 36 | 37 | ## Pre/Post Conditions 38 | | Precondition | Postcondition | 39 | | ------------ | ------------- | 40 | | Data sent to the function is a useable format to perform desired action | The thing that was intended by the function is actually done | 41 | | A condition/predicate must be true just prior to the execution of some section | A condition that must be true after the execution of some section of code | 42 | | If the precondition is violated, the effect of the code is undefined | Provided a valid precondition, we should arrive at the valid postcondition, though sometimes we'll need to check | -------------------------------------------------------------------------------- /Lessons/Week 03/15assert.md: -------------------------------------------------------------------------------- 1 | # 01/04/21: Testing and Debugging 2 | 3 | ## Assert 4 | 5 | - If the result is false, the program will terminate immediately and print an error (includes line number of error) 6 | - Asserts are useful to check if post-conditions are met 7 | 8 | ### driver.cc 9 | ``` cpp 10 | #include 11 | 12 | int kLargestPossible = 1000; 13 | int num_of_elements = -1; 14 | assert (kLargestPossible >= num_of_elements); 15 | assert (num_of_elements > 0); 16 | ``` 17 | 18 | ## error in standard out 19 | a.out: driver.cc:12: int main(): Assertion 'num_of_elements > 0' failed. 20 | 21 | ## Assert vs. Exceptions 22 | - Asserts are used to detect programming errors made by us 23 | - If we receive input from an external source, this is ideal for the use of exceptions 24 | 25 | ## Formal Testing 26 | - **Unit Testing**: 27 | - Written by programmers for programmers 28 | - The intent is to test the system at the lowest level, in isolation from the rest of the system 29 | - Ideally you should have 100% coverage, 90%+ is the goal 30 | - **Component Testing**: 31 | - Written against individual components of the system 32 | - To check that something we are building is working, like classes/struct/etc. -------------------------------------------------------------------------------- /Lessons/Week 04/16pointers.md: -------------------------------------------------------------------------------- 1 | # 02/08/22: Pointers 2 | 3 | ## Review Terms 4 | - **Type**: defines a set of possible values and a set of operations for an object 5 | - **Object**: abstraction of a memory cell that holds a value of given type 6 | - **Value**: set of bits in memory interpreted according to the type 7 | - **Variable**: name of object 8 | - **Name**: letters/digits 9 | - **Address**: variable is associated with this machine memory address 10 | - **Type**: determines range of values that can be stored 11 | - **Value**: contents of memory cell or cells associated with the variable 12 | - **Scope**: part of the program where the variable has meaning 13 | - **Lifetime**: object's lifespan from allocation to deallocation 14 | - Different objects take up different space and can store a different range of objects 15 | 16 | ## Review Declaring Variables 17 | 4 parts as follows: 18 | - **SPECIFIER** (optional), ex: const 19 | - **BASE TYPE**, ex: int 20 | - **DECLARATOR**, postfix/prefix name/symbol that tells you more about the variable 21 | - prefix for pointers: * 22 | - prefix for constant pointers: *const 23 | - prefix for reference: & 24 | - postfix for array: [] 25 | - postfix for function: () 26 | - **INITIALIZER** (optional), ex: = 5; 27 | 28 | ## Pointers 29 | - Pointer is an object, its value is the address of another object 30 | - In the example below, both p and i are variables of objects (pointer and int, respectively) and so have their own addresses. The point of a pointer is to get the address of i and store it as the value of p 31 | 32 | ``` cpp 33 | int* p = nullptr; 34 | // p is a pointer to an integer object 35 | // initialized here with null pointer, p currently points to nothing 36 | 37 | int i = 7; 38 | p = &i; 39 | // address of i assigned to p, p currently points to i 40 | ``` 41 | 42 | ## Making an indirect assignment through pointer 43 | | **&p** | **p** | **\*p** | 44 | | -- | - | -- | 45 | | 0x7ffc73fa460 | 0x7ffc73fa467c | 7 | 46 | 47 | Here, * is a unary operator in an expression, not variable declarator! It is dereferencing. It looks at pointer p -> goes to address of p -> gets that value of the object pointer points to 48 | 49 | ``` cpp 50 | int i = 7; 51 | int* p = &i; 52 | int j = *p; 53 | // int i stores 7, int j stores 7 54 | *p = 4; 55 | // int i stores 4, int j stores 7 56 | ``` 57 | 58 | ## Declarator vs Operator (* and &) 59 | 60 | ### & operator. r stores reference to i 61 | ``` cpp 62 | int i = 11; 63 | int& r = i; 64 | ``` 65 | 66 | ### * declatator, & operator. pointer p stores address of i 67 | ``` cpp 68 | int i = 11; 69 | int* p = &i; 70 | ``` 71 | 72 | ### * declarator, & operator, * operator. 73 | ``` cpp 74 | int i = 11; 75 | int* p = &i; 76 | *p = 2; 77 | ``` 78 | -------------------------------------------------------------------------------- /Lessons/Week 04/17arrays.md: -------------------------------------------------------------------------------- 1 | # 02/08/22: Arrays 2 | 3 | - These arrays are stored in the **stack** 4 | - Understanding arrays helps us better understand std::vector 5 | 6 | ## Declaration 7 | - **ALWAYS** store the size of the array in a variable when you declare because **arrays do not know their size!** 8 | - They do not have any behavior to be called 9 | - The size of arrays must be constant and known at compile time 10 | 11 | ``` cpp 12 | // create array x will store 7 int elements 13 | constexpr int x_size = 7; 14 | int x[x_size]; 15 | 16 | // declare and initialize array y 17 | int y[] = {1, 2, 10, 3}; 18 | int y_size = 4 19 | ``` 20 | 21 | ## Access 22 | ``` cpp 23 | int first_element = x[0]; 24 | int last_element = x[x_size - 1]; 25 | ``` 26 | 27 | ## Hack for finding array size 28 | This formula divides the byte size of the entire array by the byte size of the first element, since all elements are of the same type this is shoyld give you the size of the array. **This only works in the same code block where the array is declared though**. 29 | ``` cpp 30 | int x[] = {1, 1, 2, 2}; 31 | constexpr int size_x = sizeof(x) / sizeof(x[0]); 32 | ``` 33 | 34 | ## Passing arrays 35 | - When passed to a function, arrays decay into a pointer and loses all information 36 | - We can still interact with the actual array using pointer arithmetic 37 | - The address to the 0th element is stored in the name of the array when decayed, we can move the pointer along the array from element 0 to element i (whichever index we want), dereference int at that index in the array, and access it 38 | 39 | ## Array Limitations 40 | - Size not known 41 | - Potentially waste space or run out of space when we create arrays with unknown size 42 | - Current best method is to use std::vector and .push_back() 43 | - Array indices are not checking before accessing, so if we access an index out of range, it won't alert us with an error, it will just keep running -------------------------------------------------------------------------------- /Lessons/Week 04/18references.md: -------------------------------------------------------------------------------- 1 | # 02/10/22: References and argument passing 2 | 3 | ## References 4 | **Pass by value** 5 | - This is when the parameter is an object and a value is copied into the parameter object for initialization 6 | - Changes made in the function do not change argument because we are working with copies created at parameter initialization 7 | ``` cpp 8 | int add(int a, int b) { return a + b; } 9 | 10 | int main() { 11 | int i = 4; 12 | int j = 6; 13 | int k = add(i, j); 14 | } 15 | ``` 16 | 17 | **Swap variables** 18 | - Works when swap occurs in main() 19 | ``` cpp 20 | int main() { 21 | int i = 1; 22 | int j = 2; 23 | 24 | int temp = i; 25 | i = j; 26 | j = temp; 27 | } 28 | ``` 29 | 30 | - Passing by value doesn't work with swap function!!! The code below doesn't actually swap the values of int i and j 31 | ``` cpp 32 | int Swap(int a, int b) { 33 | int temp = a; 34 | a = b; 35 | b = temp; 36 | } 37 | 38 | int main() { 39 | int i = 1; 40 | int j = 2; 41 | swap(i, j); 42 | } 43 | ``` 44 | 45 | Can functions change the actual value of variables? YES! This is where we use pass by reference 46 | 47 | ## Creating references 48 | - Reference: an alias or nickname for an object 49 | - They both refer to the SAME object (same address) 50 | ``` cpp 51 | int i = 1; 52 | int& ii = i; // reference for i 53 | int j = 2; 54 | const int jj = j; // constant reference, read-only 55 | ``` 56 | 57 | All references MUST be initialized at the point of declaration because they cnanot be re-referenced to another object! **Compilation error:** 58 | ``` cpp 59 | int& a; 60 | ``` 61 | 62 | References cannot bind to literals! Only variables of objects. 63 | ``` cpp 64 | int& a = 11; // error 65 | ``` 66 | 67 | We are allowed to take a reference to const to a literal though: 68 | ``` cpp 69 | const int& a = 11; 70 | ``` 71 | 72 | ## Pass by reference 73 | In this example, changes made in the passed reference will always be reflected in the actual objects bound to the reference. 74 | ``` cpp 75 | void swap(int& a, int& b) { 76 | int temp = a; 77 | a = b; 78 | b = temp; 79 | } 80 | 81 | int main() { 82 | int i = 1; 83 | int j = 2; 84 | swap(i, j); 85 | } 86 | ``` 87 | 88 | ## When to pass by reference 89 | - Modify arguments that are bound to reference, ex: swap function 90 | - Avoid copies, especially with objects become really big or some types (like IO) cannot be copied 91 | 92 | ## Guides for passing arguments 93 | - Use **pass by value** for small objects, primitive types, a simple copy is effective 94 | - Use **pass by reference to const** for large objects, ex: std::vector and std::string 95 | - Use **"vanilla" pass by reference** only when you REALLY need to modify the variable of the object itself. Favor returning result over this. -------------------------------------------------------------------------------- /Lessons/Week 04/19function_overloading.md: -------------------------------------------------------------------------------- 1 | # 02/11/22: Function overloading 2 | 3 | - Compiler decides which function to call by comparing arguments 4 | - Exact match (int passed, int found) 5 | - Match through promotion (char passed, int found) 6 | - Match using standard conversions (int passed, double found) 7 | - If there isn't an exact call, and the compiler finds multiple compatible matches, it will report an **ambiguous call error** 8 | 9 | ``` cpp 10 | bool AreEqual(int a, double b); 11 | bool AreEqual(double a, int b); 12 | 13 | // call made 14 | AreEqual(2.5, 3.4); 15 | 16 | // results in error: call to 'AreEqual' is ambiguous 17 | ``` 18 | 19 | ## Guidance 20 | - Use overloading when functions perform same operation (name same) but needs to handle significantly different data types 21 | - Else, construct functions with different names -------------------------------------------------------------------------------- /Lessons/Week 04/20language_agnostic_intro_to_userdefined_types.md: -------------------------------------------------------------------------------- 1 | # 02/11/22: Language agnostic introduction to user-defined types 2 | 3 | ## Abstraction 4 | - Think about a car! 5 | - You can think about its attributes (color, horsepower) 6 | - You can think about its behavior (lights on, parked) 7 | - Some information is more important than other information, but there isn't a specific way to handle the uninformation information 8 | - **Abstraction process**: denotes extraction of important information while ignoring unimportant information 9 | - **Abstraction entity**: denotes model, view, other focused representation for an item 10 | 11 | ## Types 12 | Types are helpful because they provide 13 | - **Representation**: knows how to represent the data needed in an object => **USER-DEFINED TYPE/VARIABLES** 14 | - **Operations**: knows operations to be applied to objects => **FUNCTIONS** 15 | - User-defined types provide a blueprint for how objects are created, used, and destroyed 16 | 17 | ## Encapsulation 18 | - **Encapsulation process**: encloses items in container 19 | - **Encapsulation entity**: package/enclosure that holds enclosed items 20 | 21 | ## Interfaces 22 | - Hides implementation while still providing user with details on **what** the object does instead of **how** it does it -------------------------------------------------------------------------------- /Lessons/Week 05/21classes.md: -------------------------------------------------------------------------------- 1 | ## 02/14/22: Introduction to classes 2 | 3 | ## What is a class? 4 | - User-defined type that has representation:operations::attributes:behaviors 5 | - Class is a template from which objects of the class-type can be created 6 | 7 | ## struct vs. class 8 | - Members of a struct are PUBLIC by default, can be accessed with dot notation (c.r) 9 | - structs are used in data structures where the members can take any value 10 | ``` cpp 11 | struct Color { 12 | int r; 13 | int g; 14 | int b; 15 | } 16 | ``` 17 | 18 | - Members of a class are PRIVATE by default, cannot be accessed with dot notation (c.r) 19 | ``` cpp 20 | class Color { 21 | int r_; 22 | int g_; 23 | int b_; 24 | } 25 | ``` 26 | 27 | ## Writing a class 28 | - **accessor**: function that returns the value stored in a private data member 29 | - **mutator**: function that stores a value in private data member or mutates its state 30 | - Prefix function name like so "class_name::function_name", :: is called the score resolution operator 31 | 32 | ## C++ class structure 33 | ### color.hpp 34 | ``` cpp 35 | #ifndef COLOR_HPP 36 | #define COLOR_HPP 37 | 38 | class Color { 39 | public: 40 | // include elements of Color's interface 41 | // any functions in any class can access these public data members 42 | void r(int rr); 43 | void g(int gg); 44 | void b(int bb); 45 | int r() const; 46 | int b() const; 47 | int g() const; 48 | 49 | private: 50 | // include elments of Color's implementation 51 | // private data members can only be accessed by members that are part of that class 52 | 53 | // convention to mark private local variables followed by an underscore 54 | int r_; 55 | int g_; 56 | int b_; 57 | // static marks that these variables are shared across all instances of this class 58 | static constexpr int kMaxColorValue = 255; 59 | static constexpr int kMinColorValue = 0; 60 | }; 61 | #endif 62 | ``` 63 | ### color.cpp 64 | ``` cpp 65 | #include "color_hpp" 66 | 67 | // mutator 68 | void Color::r(int rr) { 69 | if (ValidColorValue(rr)) { 70 | r_ == rr; 71 | } else { 72 | throw std::runtime_error("Invalid Value"); 73 | } 74 | } 75 | 76 | // accessors 77 | // const appears after call to indicate that function will not change state of object for which it is called 78 | int Color::r() const {return r_; } 79 | int Color::g() const {return g_; } 80 | int Color::b() const {return b_; } 81 | 82 | // helper function 83 | bool Color::ValidColorValue(int value) const { 84 | if (value >= kMinColorValue && value <= kMaxColorValue) { 85 | return true; 86 | } else { 87 | return false; 88 | } 89 | } 90 | ``` 91 | 92 | ### main.cpp 93 | ``` cpp 94 | int main() { 95 | Color c; 96 | c.r(255); 97 | std::cout << c.r() << std::endl; // prints 255 98 | c.r(11); 99 | std::cout << c.r() << std::endl; // prints 11 100 | c.r(-30); // THROWS ERROR: INVALID VALUE 101 | return 0; 102 | } 103 | ``` 104 | -------------------------------------------------------------------------------- /Lessons/Week 05/22more_classes.md: -------------------------------------------------------------------------------- 1 | # 02/15/22: Introduction to Classes, cont. 2 | 3 | ## Constructors, Destructors 4 | - Object initialization doesn't automatically initialize the object's built-in types 5 | - Constructors help us guarantee valid values for these built-in types 6 | 7 | ### Default Constructor 8 | File: color.hpp 9 | ``` cpp 10 | #ifndef COLOR_HPP 11 | #define COLOR_HPP 12 | 13 | class Color { 14 | public: 15 | Color(); 16 | private: 17 | int r_; 18 | int g_; 19 | int b_; 20 | }; 21 | #endif 22 | ``` 23 | 24 | File name: color.cpp 25 | ``` cpp 26 | #include "color.hpp"; 27 | 28 | Color::Color(): r_(0), g_(0), b_(0) { 29 | // values initialized using the constructor 30 | // initializer list 31 | } 32 | 33 | // define other functions 34 | ``` 35 | 36 | ### Parametrized constructors 37 | File name: color.hpp 38 | ``` cpp 39 | Color(int r, int g, int b); 40 | ``` 41 | 42 | File name: color.cpp 43 | ``` cpp 44 | Color::Color(int r, int g, int b): r_(r), g_(g), b_(b) { 45 | // check that assigned r_, g_, b_ values are valid 46 | } 47 | ``` 48 | 49 | ## DO NOT use C++ assignment, use INITIALIZATION!!! 50 | File name: color.cpp 51 | ``` cpp 52 | #include "color.hpp" 53 | 54 | Color::Color() { 55 | // old C++ practices, use the above initializer list instead 56 | r_ = 0; 57 | b_ = 0; 58 | g_ = 0; 59 | } 60 | ``` 61 | 62 | ## Prevent narrowing conversion 63 | File name: color.cpp 64 | ``` cpp 65 | #include "color.hpp" 66 | 67 | Color::Color() : r_{0}, g{0}, b{0} { 68 | // check variable validity 69 | } 70 | ``` 71 | 72 | ## Public / Private Distinction 73 | - Clean interface 74 | - Easy debugging, only fixed set of functions can access data 75 | 76 | **Guidance for attributes** 77 | - Public = the value assigned will work regardless 78 | - Private = the value assigned to an object must be checked/conform to some requirements 79 | 80 | ## Interfaces 81 | Good interface 82 | - Small as possible, easy to understand/debug/maintain 83 | - Complete 84 | - Type safe, no confusing argumnet orders 85 | - **const** correct 86 | 87 | ## Private member functions 88 | - These can only be called by other functions in that class 89 | - Keep public interface as minimal as possible 90 | 91 | ## Non-member helper function 92 | - These do not access the representation of the instantiated object 93 | -------------------------------------------------------------------------------- /Lessons/Week 05/23operator_overloading.md: -------------------------------------------------------------------------------- 1 | # 02/16/22: Operator Overloading 2 | 3 | - Overloaded opeator has the same number of parameters as operator has operands, number of paramters defines unary vs binary 4 | - We can only overload existing operators, cannot invent new ones 5 | 6 | ## Calling non-member overloaded operator 7 | Using the operator on arguments of suitable type 8 | - something + something_else 9 | 10 | Using the familiar function call syntax 11 | - operator + (something, something_else) 12 | 13 | ## Calling member overloaded operator 14 | Frequently used 15 | - something + something_else 16 | - something.operator+(something_else) 17 | 18 | ## Guidelines for member vs non-member operators 19 | **MEMBER** 20 | - Assignment (=), subscript([]), call(()), and member access (->) 21 | - Compound assignment operators (+=, -=, etc.) 22 | - Operators that change state of object 23 | **NON-MEMBER** 24 | - Symmetric operators defined as non-members 25 | - We want to use symmetric operators in expressions with mixed types 26 | 27 | ## Example 1: overloading operator = 28 | File name: color.hpp 29 | ``` cpp 30 | #ifndef COLOR_HPP 31 | #define COLOR_HPP 32 | 33 | class Color { 34 | public: 35 | Color& operator=(const Color& rhs); 36 | 37 | // etc... same definitions as before 38 | }; 39 | 40 | #endif 41 | ``` 42 | 43 | File name: color.cpp 44 | ``` cpp 45 | #include "color.hpp" 46 | 47 | // rhs is color object that is being passed 48 | // indicate member function by adding "Color::" 49 | Color& Color::operator=(const Color& rhs) { 50 | // since part of color class, this function has raw access to private data members r_, g_, b_ 51 | r_ = rhs.r_; 52 | g_ = rhs.g_; 53 | b_ = rhs.b_; 54 | // convention: return self reference to object 55 | return *this; 56 | } 57 | 58 | // etc... other definitions 59 | ``` 60 | 61 | ## Example 2: overloading operator += 62 | File name: color.hpp 63 | ``` cpp 64 | #ifndef COLOR_HPP 65 | #define COLOR_HPP 66 | 67 | class Color { 68 | public: 69 | Color& operator+=(const Color& rhs); 70 | // etc... same definitions as before 71 | }; 72 | 73 | #endif 74 | ``` 75 | 76 | File name: color.cpp 77 | ``` cpp 78 | #include "color.hpp" 79 | 80 | Color& Color::operator+=(const Color& rhs) { 81 | r_ (r_ += rhs.r_) / 2; 82 | g_ (g_ += rhs.g_) / 2; 83 | b_ (b_ += rhs.b_) / 2; 84 | return *this; 85 | } 86 | 87 | // etc... other definitions 88 | ``` 89 | 90 | ## Example 3: overloading operator-- (postfix) 91 | File name: color.hpp 92 | ``` cpp 93 | #ifndef COLOR_HPP 94 | #define COLOR_HPP 95 | 96 | class Color { 97 | public: 98 | Color& operator--(int); 99 | // etc... same definitions as before 100 | }; 101 | 102 | #endif 103 | ``` 104 | 105 | File name: color.cpp 106 | ``` cpp 107 | #include "color.hpp" 108 | 109 | Color& Color::operator--(int) { 110 | Color old_state = *this; 111 | // do something, define yourself based on what you think behavior should look like 112 | return old_state; 113 | } 114 | 115 | // etc... other definitions 116 | ``` 117 | 118 | ## Example 4: overloading operator + 119 | File name: color.hpp 120 | ``` cpp 121 | #ifndef COLOR_HPP 122 | #define COLOR_HPP 123 | 124 | class Color { 125 | public: 126 | // definitions 127 | private: 128 | // definitions 129 | }; 130 | 131 | // since declaration outside class, we pass both args 132 | Color operator+(const Color& lhs, const Color& rhs); 133 | 134 | #endif 135 | ``` 136 | 137 | File name: color.cpp 138 | ``` cpp 139 | #include "color.hpp" 140 | 141 | Color operator+(const Color& lhs, const Color& rhs) { 142 | Color blend; 143 | // non-member function, use accessors and mutators 144 | blend.r( (lhs.r() + rhs.r()) / 2 ); 145 | blend.g( (lhs.g() + rhs.g()) / 2 ); 146 | blend.b( (lhs.b() + rhs.b()) / 2 ); 147 | return blend; 148 | } 149 | 150 | // etc... other definitions 151 | ``` 152 | 153 | ## Example 5: overloading operator << 154 | File name: color.hpp 155 | ``` cpp 156 | #ifndef COLOR_HPP 157 | #define COLOR_HPP 158 | 159 | #include 160 | 161 | class Color { 162 | public: 163 | // definitions 164 | private: 165 | // definitions 166 | }; 167 | 168 | // insertion operators must be defined as on-member 169 | std::ostream& operator<<(std::ostream& os, const Color& color); 170 | 171 | #endif 172 | ``` 173 | 174 | File name: color.cpp 175 | ``` cpp 176 | #include "color.hpp" 177 | 178 | // returning reference to stream 179 | std::ostream& operator<<(std::ostream& os, const Color& color) { 180 | os << color.r() << '\t'; 181 | os << color.g() << '\t'; 182 | os << color.b(); 183 | return os; 184 | } 185 | 186 | // etc... other definitions 187 | ``` -------------------------------------------------------------------------------- /Lessons/Week 05/24streams.md: -------------------------------------------------------------------------------- 1 | # 02/17/22: Streams and Input Validation 2 | 3 | ## Reading from a Stream 4 | - The operator<< is white space delimited 5 | 6 | ### Example 1: string 7 | ``` cpp 8 | std::std first_name; 9 | std::string last_name; 10 | std::cout << "Enter your name: "; 11 | std::cin >> first_name >> last_name; 12 | ``` 13 | 14 | If the user enters "michael nowak", **std::cin reads it like this** 15 | | m | i | c | h | a | e | l | | n | o | w | a | k | \n | 16 | 17 | The variables are stored as follows: 18 | 19 | - **first_name** 20 | | m | i | c | h | a | e | l | 21 | 22 | - **last_name** 23 | | n | o | w | a | k | 24 | 25 | ### Example 2: decimal 26 | ``` cpp 27 | int whole_part; 28 | char throw_away; 29 | int fractional_part; 30 | std::cout << "Enter floating-point number: "; 31 | std::cin >> whole_part >> throw_away >> fractional_part; 32 | ``` 33 | 34 | If the user enters 3.14, the variables will be fillied as follows: 35 | 36 | - **whole_part** 37 | | 3 | 38 | 39 | - **throw_away** 40 | | . | 41 | 42 | - **demical_part** 43 | | 1 | 4 | 44 | 45 | ## Format Read Errors 46 | Use __**stream states**__ to your advantage when reading from ifs file 47 | - __**ifs.good()**__ = true if the previous read succeeded, no errors in stream 48 | - __**ifs.bad()**__ = true if unrecoverable error occured 49 | - __**ifs.fail()**__ = true if either unrecoverable error or formating/recoverable error occured (check in comparison with ifs.bad()) 50 | 51 | ## Recover from a Format Read Error 52 | 53 | - __**ifs.clear()**__ = the state of the stream resets to default 54 | - good() is true 55 | - bad() is false 56 | - fail() is false 57 | 58 | - __**ifs.ignore(1, '\n')**__ = ignores the character that is mismatched, flow by you and ignore it 59 | - first arg = # of chars to ignore 60 | - second arg = ignore till # or chars or till next whitespace 61 | 62 | - Use the following code to ignore all characters until specific character is observed 63 | ``` cpp 64 | #include 65 | 66 | ifs.ignore(std::numeric_limits::max(), '\n'); 67 | ifs.ignore(std::numeric_limits::max(), '\t'); 68 | std::cin.ignore(std::numeric_limits::max(), ';'); 69 | ``` -------------------------------------------------------------------------------- /Lessons/Week 06/25data_representation.md: -------------------------------------------------------------------------------- 1 | # 02/21/22: Data representation 2 | 3 | ## Positional number system 4 | - Any positive integer b > 1 can be chosen as base 5 | - decimal (b = 10) 6 | - binary (b = 2) 7 | - The value that a digit contributes is based on it's position 8 | - Decimal Ex: 124 = 1(10^2) + 2(10^1) + 4(10^0) = 1(100) + 2(10) + 4(1) 9 | 10 | ## Binary to Decimal 11 | 1. Double the leftmost digit 12 | 2. Add the result to the next digit on the right 13 | 3. Double that sum 14 | 4. Add the result to the next digit 15 | 5. Repeat until the last integral digit is added, final sum is equivalent 16 | 17 | ## Decimal to Binary 18 | 1. Subtract the largest possible power of base-2 from N1 19 | 2. Subtract the largest possible power of base 2 from the result 20 | 3. Continue this process until a difference of zero is obtained 21 | 4. Place a bit value of 1 in place values of those powers subtracted and a bit value of 0 elsewhere 22 | 23 | ## Binary Addition Facts 24 | - 0 + 0 = 0 25 | - 1 + 0 = 1 26 | - 0 + 1 = 1 27 | - 1 + 1 = 0 (carry forward 1) 28 | - 1 + 1 + 1 = 1 (carry forward 1) 29 | -------------------------------------------------------------------------------- /Lessons/Week 06/26data_representation_cont.md: -------------------------------------------------------------------------------- 1 | # 02/22/22: Data representation cont. 2 | 3 | - Store negative numbers as their additive inverse of their positive counterpart 4 | - If you are adding a positve integer (like 3) to a negative one (like -3), note that the summation doesn't have to add up to 4 bits of 0, you just have to have the last 4 bits as 0 - the rest can be manipulated to make addition easier. 5 | 6 | **Ex: adding 3 + (- 3)** 7 | 8 | 0 0 1 1 9 | \+ 1 1 0 1 10 | .......... 11 | 1 0 0 0 0 12 | 13 | - The 1 at the front get's thrown away because it has no value (also remember in binary 1 + 1 = 0 with a 1 carry forward) 14 | - Use two's complement for negative integer numbers 15 | - Flip all digits and add 1 16 | - Largest number in 4-bit: 2^(4-1) - 1 = 7 = binary 0111 17 | - Smallest number in 4-bit: -2^(4-1) = -8 = binary 1000 18 | 19 | ## HW: 8-BIT CONVERSION DECIMAL TO BINARY (all integers) 20 | ``` cpp 21 | #include "converter.hpp" 22 | 23 | std::string Base10toBase2(int base10_value) { 24 | if (base10_value > 127 || base10_value < -128) { 25 | throw std::invalid_argument("Input out of bounds"); 26 | } 27 | // POSITIVE 28 | if (base10_value > 0) { 29 | std::string binary; 30 | int binaryNum[32]; 31 | int i = 0; 32 | while (base10_value > 0) { 33 | binaryNum[i] = base10_value % 2; 34 | base10_value = base10_value / 2; 35 | i++; 36 | } 37 | for (int j = i - 1; j >= 0; j--) { 38 | binary += std::to_string((binaryNum[j])); 39 | } 40 | std::string eightbit_binary; 41 | for (int i = 0; i < 8 - binary.length(); i++) { 42 | eightbit_binary += "0"; 43 | } 44 | eightbit_binary += binary; 45 | return "0b" + eightbit_binary; 46 | } 47 | 48 | // NEGATIVE 49 | if (base10_value < 0) { 50 | base10_value *= -1; 51 | std::string binary; 52 | int binaryNum[32]; 53 | int i = 0; 54 | while (base10_value > 0) { 55 | binaryNum[i] = base10_value % 2; 56 | base10_value = base10_value / 2; 57 | i++; 58 | } 59 | for (int j = i - 1; j >= 0; j--) { 60 | binary += std::to_string((binaryNum[j])); 61 | } 62 | std::string eightbit_binary; 63 | for (int i = 0; i < 8 - binary.length(); i++) { 64 | eightbit_binary += "0"; 65 | } 66 | eightbit_binary += binary; 67 | 68 | // find two's complement 69 | // PART 1: flip digits 70 | for (int i = 0; i < eightbit_binary.length(); i++) { 71 | if (eightbit_binary.substr(i, 1) == "0") { 72 | eightbit_binary.replace(i, 1, "1"); 73 | } else if (eightbit_binary.substr(i, 1) == "1") { 74 | eightbit_binary.replace(i, 1, "0"); 75 | } 76 | } 77 | 78 | // PART 2: add 1 79 | for (int i = eightbit_binary.length() - 1; i >= 0; i--) { 80 | std::string current = eightbit_binary.substr(i, 1); 81 | if (current == "0") { 82 | eightbit_binary.replace(i, 1, "1"); 83 | return "0b" + eightbit_binary; 84 | } 85 | eightbit_binary.replace(i, 1, "0"); 86 | } 87 | } 88 | 89 | // ZERO 90 | return "0b00000000"; 91 | } 92 | 93 | ``` -------------------------------------------------------------------------------- /Lessons/Week 07/27functions_and_stack.md: -------------------------------------------------------------------------------- 1 | # 02/28/2022: Functions and the stack 2 | 3 | ## Anatomy of program in memory 4 | When we launch our program, it is partitioned into sections that are assigned "chunks" of memory. 5 | - **Code/static** = program code, global and static vars 6 | - **Free store (heap)** = dynamically allocated objects 7 | - __***EMPTY SPACE EXISTS HERE FOR STACK/HEAP TO GROW/SHRINK INTO IT***__ 8 | - **Stack** = automatic variables = automatic vars 9 | Stack overflow is when these two opposing memory compartments collide, meaning that the stack is too full. 10 | 11 | ## The Stack 12 | - **Activation record** pushed when function called and popped when function returns 13 | - Activation record (stack frame) is responsible for 14 | - Housekeeping information 15 | - Arguments passed by value to the function 16 | - Local variables defined in the function 17 | - Ex: with recursive function calls 18 | ![Stack Function Calls](/Images/StackFunctionCall.png) 19 | - Space required for function's activation record is known at compile time, so when the function is called that memory is automatically "**allocated**" on the stack, when the function is finished executing, that memory is "**deallocated**". 20 | 21 | ## Where the function finds the activation record 22 | - Stack pointer moves in one direction as space is allocated for each function call 23 | - Stack pointer moves in the other direction as space is deallocated for each function return 24 | - Each activation record similar to heterogenous array 25 | - **In C++, the first activation record is main() call, deallocates when int is returned!!** 26 | 27 | ## Additional considerations 28 | - Stack can grow either up or down depending on where the address is placed in memory. Convention is to have it go downwards (i.e. stack starts at the upper limit and "grows down" towards 0). 29 | - Hows parameters and variables are laid out within the activation record is unspecified and unpredictable, depends on compiler -------------------------------------------------------------------------------- /Lessons/Week 07/28variables_on_the_stack.md: -------------------------------------------------------------------------------- 1 | # 03/01/2022: Allocation of memory to variables on the stack 2 | 3 | ## Overview 4 | **STACK** 5 | - memory allocation to variables (named objects) on stack 6 | - size of variables must be known 7 | - see previous lesson for activation record 8 | 9 | **FREE STORE** 10 | - dynamic allocation of objects on free store 11 | - we do not not need to know size of objects at compile-time 12 | - memory can be requested at runtime 13 | 14 | **STATIC/CODE** 15 | - memory allocation for static and global variables 16 | 17 | ## Why the size of objects stored on stack miust be known at compile-time? 18 | - If the size is known and the order in which activation record is pushed is also known **then the stack pointer is always at a fixed offset away** 19 | - This way, past function calls can be be accessed at a known and predictable pointer distance!!! 20 | - **PROBLEM**: an array with variable length is not supported by the C++ standard because we don't know how much space it needs in memory and **there is no standard offset for the stack pointer**!! 21 | - Variable lenght arrays (VLAs) are supported on C99 (C, not C++), some compilers allow you to do this because of extensions but you should not make VLAs in C++ 22 | 23 | ## Stack Limitations 24 | - **Array on stack**, we must know how to allocate for the array at compile-time, i.e. even before you read the data **==> free store takes of this later** 25 | - Stack automatically dellocates space for that object once function returns, how to preserve objects in memory? **==> free store also takes care of this later** -------------------------------------------------------------------------------- /Lessons/Week 07/29dynamic_allocation_on_free_store.md: -------------------------------------------------------------------------------- 1 | # 03/02/2022: Dynamic allocation of objects on the free store 2 | 3 | ## Dynamically allocated objects 4 | - Resides in the free store (heap) 5 | - Indicate free store object using **new** operator, this operator returns based address of region in memory allocated for that object 6 | 7 | ``` cpp 8 | int* free_store_object = new int; 9 | const size_t kFreeStoreArrayCapacity = 10; 10 | int* free_store_array = new int[kFreeStoreArrayCapacity]; 11 | ``` 12 | 13 | - We are responsible for deallocating memory once we are done using the object 14 | - **NOTE**: WE THE VARIABLE HOLDS THE ADDRESS OF THE OBJECT (it's a pointer), SO THE DELETE OPERATOR GOES TO THAT ADDRESS AND DEALLOCATES MEMORY 15 | ```cpp 16 | delete free_store_object; 17 | delete[] free_store_array; 18 | ``` 19 | 20 | To avoid narrowing conversion 21 | ``` cpp 22 | int* p_int = new int{0}; 23 | double* p_double = new double{0.0}; 24 | ``` 25 | 26 | ## What's on the stack vs free store? 27 | - Must update to **nullptr** to disconnect the pointer in the free store 28 | - Just deleting leaves you with a **DANGLING pointer** 29 | 30 | ```cpp 31 | size_t free_store_array_capacity = 4; 32 | int* free_store_array = new int[free_store_array_capacity]{10, 20, 30, 40}; 33 | delete[] free_store_array; 34 | // disconnecting pointer! 35 | free_store_array = nullptr; 36 | ``` 37 | 38 | STACK: The value of named objects 39 | - **value is 4**, identified by the int *free_store_array_capacity* 40 | - **value is address of an integer object (array)**, identified by the pointer *free_store_array* 41 | 42 | FREE STORE 43 | - space allocated {10, 20, 30, 40} 44 | 45 | ## delete Operator 46 | - delete 47 | - delete[] 48 | 49 | ## Resizing dynamically allocated array 50 | 1. Datermine the capacity/size of new array 51 | 2. Create a new array with that capacity and store address somewhere, so you do not lose the pointer 52 | 3. Copy the contents of the old array into the new array 53 | 4. delete[] old array (dangling pointer remains) 54 | 5. Update the pointer of array with address of new array 55 | 6. Update the variable storing array capacity/size 56 | 57 | ```cpp 58 | MyArray ReadDataFromFile(const std::string& file_name) { 59 | std::ifstream ifs{file_name}; 60 | MyArray data{new int[2], 2, 0}; 61 | int value_read; 62 | while (ifs >> value_read) { 63 | if (data.size == data.capacity) { 64 | ResizeMyArray(data); 65 | } 66 | data.array[data.size] = value_read; 67 | data.size += 1; 68 | } 69 | return data; 70 | } 71 | 72 | // pass by reference to actually modify the object 73 | void ResizeMyArray(MyArray& data) { 74 | size_t updated_capacity = data.capcity * 2; 75 | int* tmp = new int[updated_capacity]; 76 | for (size_t i = 0; i < data.size; i++) { 77 | temp[i] = data.array[i]; 78 | } 79 | // dangling pointer 80 | delete[] data.array; 81 | // reassign pointer 82 | data.array = temp; 83 | data.capacity = updated_capacity; 84 | } 85 | ``` 86 | 87 | ## HW Notes 88 | - Read operators right to left 89 | ```cpp 90 | int*& ptr; // this a REFERENCE to a POINTER 91 | ``` -------------------------------------------------------------------------------- /Lessons/Week 07/30implicit_free_store_contract.md: -------------------------------------------------------------------------------- 1 | # 03/03/2022: An implicit contract with the free store manager 2 | 3 | ## The contract 4 | - **CLAUSE 1**: must return memory borrowed, else **MEMORY LEAK** 5 | - **CLAUSE 2**: must immediately stop using memory that is returned, else **DANGLING POINTER** 6 | - **CLAUSE 3**: should not return memory that is not borrowed/return memory twice that is borrowed once, else **RUNTIME ERROR** 7 | 8 | ## Clause 1 9 | Memory leaks occur when we dynamically allocate memory for an object, but fail to ever deallocate it 10 | 11 | **__Incorrect way to deallocate:__** problematic because we have no way to access 7 and deallocate it!! 12 | ```cpp 13 | // p is a pointer to a pointer to an int 14 | int** p = new int*(new int(7)); 15 | delete p; 16 | p = nullptr; 17 | ``` 18 | **__Correct way to deallocate:__** delete the intermediarys first and then the address stored in p 19 | ```cpp 20 | int** p = new int*(new int(7)); 21 | delete *p; *p = nullptr; 22 | delete p; p = nullptr; 23 | ``` 24 | 25 | ## Clause 2 26 | - Dangling pointer is when you try to use a pointer to a piece of memory that has been deallocated 27 | 28 | ```cpp 29 | int* p = new int(7); 30 | delete p; // dangling pointer 31 | p = nullptr; // no longer dangling! 32 | ``` 33 | 34 | ## Clause 3 35 | ```cpp 36 | // single pointer p 37 | int* p = new int(7); 38 | delete p; 39 | delete p; // ERROR 40 | 41 | // double pointers to same object 42 | int* p = new int(7); 43 | int* op = p; 44 | delete op; 45 | op = nullptr; 46 | delete p; // ERROR because pointer was deallocated 47 | ``` -------------------------------------------------------------------------------- /Lessons/Week 07/31freestore_2darrays.md: -------------------------------------------------------------------------------- 1 | # 03/03/2022: Two-dimensional arrays on the free store 2 | 3 | ## Pointer Review 4 | - Pointer stores an address to an object 5 | - Writing "new int" returns address from free store 6 | - Pointer and new operator go hand in hand 7 | 8 | ```cpp 9 | // POINTER TO A POINTER TO AN INT 10 | int** p = new int* {new int{7}}; 11 | 12 | /* 13 | above is the same as 14 | int** p = new int*; 15 | *p = new int{7}; 16 | */ 17 | ``` 18 | 19 | ## 2d arrays on free store 20 | 21 | ## Passing 2D arrays 22 | - Requires you to pass row and col dimensions because arrays are not aware of their size 23 | - Pass the inner array of pointers instead of a pointer to the array of pointers! -------------------------------------------------------------------------------- /Lessons/Week 07/32memory_testing_debugging_tools.md: -------------------------------------------------------------------------------- 1 | # 03/04/2022: Tools of testing and debugging memory issues 2 | 3 | ## Memory Safety 4 | **Out of Bounds** 5 | - Occurs when accessing address beyond allocation 6 | - Causes crashes/corrupt other data 7 | ```cpp 8 | const unsigned int kArraySize = 10; 9 | int* array = new int[kArraySize]; 10 | // out of bounds occurs because <= array size (OFF BY ONE ERROR) 11 | for (unsigned int i = 0; i <= kArraySize; i++) { 12 | array[i] = 0; 13 | } 14 | ``` 15 | 16 | **Use After Free** 17 | - Attempting to access memory after deallocation 18 | - Write to freed memory gets lost, read from freed memory is garbage 19 | ```cpp 20 | struct Node { 21 | int data = 0; 22 | Node* next = nullptr; 23 | }; 24 | 25 | Node* n1 = new Node{1}; 26 | n1->next = new Node{2}; 27 | while (n1 != nullptr) { 28 | delete n1; 29 | n1 = n1 -> next; 30 | } 31 | ``` 32 | - Pointers and arrays touch hardware directly; segmentation fault, bus error, core dumped = uninitialized or invalid pointer 33 | 34 | **Memory Leak** 35 | - Causes memory usage to balloon while program is open 36 | - Problematic in long-running programs 37 | - Array off by one error when not initialized on the free store, the typical out of bounds error is not thrown 38 | - Forgetting to return a value 39 | 40 | ## Tools 41 | 42 | ### ASan 43 | - AddressSanitizer (ASan) 44 | - Detects out of bounds, use-after-free, use-after-return, double-free/invalid-free 45 | ``` 46 | clang++ ... -g -fsanitize=address,null -fno-omit-frame-pointer 47 | ``` 48 | 49 | ### UBSan 50 | - Detects undefined behavior: null pointers, array out of bounds, divide by zero, interger overflow, implicit conversions 51 | ``` 52 | clang++ ... -g -fsanitize=undefined 53 | ``` 54 | 55 | ### Valgrind 56 | - Detects memory leaks (memcheck), invalid pointer use, uninitialized variables 57 | - Standalone tool not in compiler, use while running code 58 | - Slows execution between 10-50x 59 | ``` 60 | clang++ ... -g 61 | ``` 62 | **Use specific tool when you run the code** 63 | ``` 64 | valgrind --tool=memcheck program_name 65 | ``` -------------------------------------------------------------------------------- /Lessons/Week 08/33RAII_dynamic_memory_in_classes.md: -------------------------------------------------------------------------------- 1 | # 03/07/2022: RAII and dynamic memory in classes 2 | 3 | ## What is RAII? 4 | - RAII = Resource Acquisition is Initialization 5 | - Resource management in C++ is initialization (construction) and clean up (destruction) based on scope 6 | - Objects with score are destructed when code exits the scope 7 | - Objects created using **new**, on the free store, need to be cleaned up by us using **delete** 8 | 9 | ## example: VectorInt 10 | Simplified version of std::vector 11 | 12 | **Header File** 13 | ```cpp 14 | #ifndef VECTORINT_HPP 15 | #define VECTORINT_HPP 16 | 17 | class VectorInt { 18 | public: 19 | VectorInt(size_t size, int init_value); 20 | ~VectorInt(); 21 | size_t size() const { return size_; } 22 | size_t capacity() const { return capacity_; } 23 | int& operator[](size_t index); 24 | const int& operator[](size_t index) const; 25 | VectorInt& push_back(int value); 26 | 27 | private: 28 | void InitArray(int init_value); 29 | void Grow(); 30 | void Resize(size_t new_capacity); 31 | int* array_; 32 | size_t size_; 33 | size_t capacity_; 34 | }; 35 | 36 | #endif 37 | ``` 38 | 39 | **C++ File** 40 | ```cpp 41 | #include "vectorint.hpp" 42 | 43 | VectorInt::VectorInt(size_t size, int init_value) : array_(nullptr), size_(size), capacity_(size*2) { 44 | try { 45 | // maybe there is no memory avaliable 46 | array_ = new int[capacity_]; 47 | } catch (const std::bad_alloc&) { 48 | // no need to clean up since first and only dynamically allocated object 49 | // just rethrow expection 50 | // useful when you have 2 dynamically allocated objects and first works but second throws error - you handle it by creating space for it 51 | throw; 52 | } 53 | InitArray(init_value); 54 | } 55 | 56 | void VectorInt::InitArray(int init_value) { 57 | for (size_t index = 0; index < size_; index++) { 58 | array[index] = init_value; 59 | } 60 | } 61 | 62 | // no return type for destructor, just like constructors 63 | VectorInt::~VectorInt() { 64 | delete[] array_; 65 | } 66 | 67 | VectorInt& VectorInt::push_back(int value) { 68 | if (size == capacity_) Grow(); 69 | array_[size_] = value; 70 | size_ += 1; 71 | // return self reference 72 | return *this; 73 | } 74 | 75 | void VectorInt::Grow() { 76 | Resize(capacity_ * 2); 77 | } 78 | 79 | void VectorInt::Resize(size_t new_capacity) { 80 | int* copy = new int[new_capacity]; 81 | int index_to_copy_till = 0; 82 | if (new_capacity > size_) { 83 | index_to_copy_till = size_; 84 | } else { 85 | index_to_copy_till = new_capacity; 86 | } 87 | for (size_t i = 0; i < index_to_copy_till; i++) { 88 | copy[i] = array_[i]; 89 | } 90 | delete[] array_; 91 | array_ = copy; 92 | size_ = index_to_copy_till; 93 | capacity_ = new_capacity; 94 | } 95 | ``` -------------------------------------------------------------------------------- /Lessons/Week 08/34copy_semantics.md: -------------------------------------------------------------------------------- 1 | # 03/08/2022: Copy Semantics 2 | 3 | ## Default copy semantics 4 | - Copies over all data members' values 5 | - For arrays/objects (dynamically allocated objects), the address is copied over, AKA pointer is moved 6 | - This is a shallow copy because both objects share one dynamically allocated object 7 | 8 | ## Shallow vs. Deep Copy 9 | - Default copy mechanism is **shallow copy**, i.e. object is copied exactly as is 10 | - In a **deep copy**, objects pointed to are copied first 11 | - After deep copy, each identifier has same values, stored in distinct objects, each gets its own dynamically allocated array 12 | 13 | ## Implementing Deep Copy 14 | **Copy Constructor for 2d array** 15 | ```cpp 16 | Array2D::Array2D(const Array 2D& other) { 17 | if (rhs.array_ == nullptr) { 18 | Array2D(); 19 | } else { 20 | width_ = other.width_; 21 | height_ = other.height_; 22 | array_ = nullptr; 23 | try { 24 | array_ = new int[width_ * height_]; 25 | } catch (...) { 26 | throw; 27 | } 28 | for (int i = 0; i < (width_ * height*); i++) { 29 | array_[i] = other.array_[i]; 30 | } 31 | } 32 | } 33 | ``` 34 | 35 | **Copy Assignment Operator** 36 | ```cpp 37 | Array2D& Array2D::operator=(const Array2D& other) { 38 | // first ALWAYS check for self assignment 39 | if (this == &rhs) { 40 | return *this; 41 | } 42 | if (other.array_ == nullptr) { 43 | Array2D(); 44 | } 45 | int* temp = nullptr; 46 | try { 47 | // if 0, leave as nullptr 48 | if (other.height_ != 0 && other.width_ != 0) { 49 | temp = new int[other.height_ * other.width_]; 50 | } 51 | } catch (...) { 52 | throw; 53 | } 54 | delete[] array_; 55 | array_ = temp; 56 | width_ = other.width_, height = other.height_; 57 | // copy over elements 58 | for (int i = 0; i < (width_ * height_); i++) { 59 | array_[i] = other.array_[i]; 60 | } 61 | // return self reference 62 | return *this; 63 | } 64 | ``` 65 | 66 | **Destructor** 67 | ```cpp 68 | Array2D::~Array2D() { delete[] array_; } 69 | ``` 70 | 71 | | **Copy Constructor** | **Copy Assignment Operator** | 72 | | ---------------- | ------------------------ | 73 | | Returns: nothing | Returns: self-reference | 74 | | Member function | operator= overload | 75 | | Parameter: reference to same type const object | Parameter: reference to same type const object | 76 | | Initialize data members -> allocate new memory (check errors) -> deep copy from source | Check for self-assignment -> allocate new memoery (check errors) -> deallocate old dynamically allocated array -> initialize data members -> deep copy from source -> return self reference | 77 | 78 | ## C++ Rule of Zero 79 | - C++ compiler provides default operators with default semantics 80 | - **If you can avoid defining default operation (copy constructor, copy operator, destructor) you should do so** 81 | - If you don't need them, don't define them! 82 | 83 | ## C++ Rule of Three 84 | - If either copy constructor, copy assignment, or destructor is defined, **you should imeplement other 2 as well** 85 | - If you want deep copy, you need to implement that behavior across all the functions 86 | - If you implement one, implement all! -------------------------------------------------------------------------------- /Lessons/Week 08/35dynamic_structures.md: -------------------------------------------------------------------------------- 1 | # 03/09/2022: Dynamic Structures 2 | 3 | ## Limitations of VectorInt 4 | - push_back when size = capacity is slightly tedious 5 | - Push to front = copy all elements one index ahead and add element at open index 1 6 | - Fast to get item at index, slow to insert or delete item 7 | 8 | ## Sequence of Nodes 9 | ```cpp 10 | struct Node { 11 | int data; 12 | Node* next; 13 | } 14 | 15 | Node* head = new Node(7); 16 | // same as (*head).next 17 | head->next = new Node(11); 18 | ``` 19 | 20 | ### Adding to the end of linked list 21 | DO NOT USE HEAD TO TRAVERSE BECAUSE IT IS OUR SINGLE ENTRY POINT INTO THE LIST, WE CANNOT AFFORD TO LOSE IT!! 22 | ```cpp 23 | Node* curr_node = head; 24 | while (curr_node != nullptr) { 25 | curr_node = curr_next->next; 26 | } 27 | curr_node->next = new Node(8); 28 | ``` 29 | **Faster way to do this: store a pointer to the tail** 30 | ```cpp 31 | Node* temp = new Node(9); 32 | tail->next = temp; 33 | tail = temp; 34 | ``` 35 | 36 | ### Adding at a specific position 37 | We need a tracker to keep track of position in linked list 38 | ```cpp 39 | int position = 3; 40 | if (position != 1) { 41 | Node* current = head; 42 | for (int i = 0; i < position; i++) { 43 | current = current->next; 44 | } 45 | } 46 | Node* temp = new Node(12); 47 | temp->next = current->next; 48 | current->next = temp; 49 | if (temp->next == nullptr) { 50 | // newly added node is the last one 51 | tail = temp; 52 | } else { 53 | // adding to head 54 | Node* temp = new Node(12); 55 | temp->next = head; 56 | head = temp; 57 | } 58 | ``` 59 | 60 | ### Destructor 61 | Run through the list, pointing to head and next, deleting head each time, updating the new head to next, till you haven't reached the end of the loop (nullptr) 62 | 63 | ```cpp 64 | while (head) { 65 | ..... 66 | } 67 | ``` 68 | 69 | **Node Sequences** are 70 | - Slower to retrieve elements in sequence 71 | - Faster to add element in middle of sequence 72 | - Can grow dynamically, no need to resize 73 | 74 | ## Dynamic Structures 75 | - Dynamic: dynamic memory allocation is used in some way 76 | - Structures: use pointers to other data in some way to create data structures 77 | - **Singly Linked Lists**: head, tail, next 78 | - **Doubly Linked Lists**: head, tail, prev, next 79 | - **Simple binary tree**: root, left, right -------------------------------------------------------------------------------- /Lessons/Week 08/36singly_linked_lists.md: -------------------------------------------------------------------------------- 1 | # 03/10/2022: Singly Linked Lists 2 | 3 | ## Implement Singly Linked Lists 4 | 5 | **SinglyLinkedList header file** 6 | ```cpp 7 | class SinglyLinkedList { 8 | public: 9 | SinglyLinkedList(); 10 | ~SinglyLinkedList(); 11 | void PushBack(int value); 12 | private: 13 | // note these lay on stack 14 | // these POINT TO items in free store 15 | Node* head_; 16 | Node* tail_; 17 | }; 18 | ``` 19 | 20 | **SinglyLinkedList c++ file** 21 | ```cpp 22 | SinglyLinkedList::SinglyLinkedList() : head_(nullptr), tail_(nullptr) { 23 | // initialized 24 | } 25 | 26 | SinglyLinkedList::PushBack(int value) { 27 | // CASE 1: list is length 0 28 | if (head_ == nullptr) { 29 | head_ = tail_ = new Node(value); 30 | } 31 | // CASE 2: list contains elements already 32 | else { 33 | Node* new_node = new Node(value); 34 | tail_->next = new_node; 35 | tail_ = new_node; 36 | // new_node will be popped off the stack 37 | } 38 | } 39 | 40 | SinglyLinkedList::~SinglyLinkedList() { 41 | while(head_) { 42 | Node* next = head->next; 43 | delete head_; 44 | head_ = next; 45 | } 46 | // node: tail is not considered dangling pointer at the end of destruction 47 | } 48 | ``` 49 | 50 | ## Traversing Linked List 51 | Do not use head to traverse linked list because we have now lost all references to this list. We can never get back to where we came from. Loss of head = memory leak since we cannot call delete on it 52 | 53 | ```cpp 54 | // craft conditional based on where you need to get to 55 | 56 | while (end of the linked list) { 57 | current = current->next 58 | } 59 | 60 | while (where you need to get to) { 61 | current = current->next; 62 | } 63 | ``` -------------------------------------------------------------------------------- /Lessons/Week 09/37function_templates.md: -------------------------------------------------------------------------------- 1 | # 03/21/2022: Function Templates 2 | 3 | ## Motivation 4 | - Instead of copy pasting functions, can we create a default template type that can process all types passed to it? 5 | - Avoid redundant overloaded functions 6 | - Avoid copy pasting in code, instead we give this task to the compiler to figure out 7 | - **__PERFORM FIND AND REPLACE__**: Compiler will generate code from **function template** by replacing **template type** with **concrete type** 8 | 9 | ## Function templates 10 | ```cpp 11 | template 12 | T MinVal(T a, T b) { 13 | return (b > a) ? a : b; 14 | } 15 | ``` 16 | 17 | **typename** 18 | - keyword that introduces a type parameter 19 | - T represents arbitary type 20 | - any type can be used as long as its behavior is template defined 21 | - **T must support operator ">"** 22 | 23 | **Instantiation** = process of replacing template parameter by concrete types 24 | 25 | ## Template Argument Deduction 26 | The following calls will result in an error because they pass 2 different types 27 | ``` cpp 28 | MinVal(2, 2.4); 29 | MinVal(2.4, 2); 30 | MinVal(2, 'a'); 31 | ``` 32 | 33 | How to handle this? 34 | 1. Cast arguments so they are the same type 35 | ```cpp 36 | MinVal(static_cast (2), 2.4); 37 | ``` 38 | 2. Explicitly state what types T can hold 39 | ```cpp 40 | MinVal(2, 2.4); 41 | ``` 42 | 3. Specify strict type in function template, let compiler figure out return type 43 | ```cpp 44 | template 45 | auto MinVal(T1 a, T1 b) { ... } 46 | ``` 47 | 48 | ## Templates and Separate Compilation 49 | - The definition of function templates are often not available until link time 50 | - BUT we cannot wait till link time to access the implementation details 51 | - **THUS** in this class, we will define the function template in the header files (.hpp) 52 | 53 | **Min.hpp** 54 | ```cpp 55 | #ifndef MIN_H 56 | #define MIN_H 57 | // declation 58 | template 59 | T MinVal(T a, T b); 60 | 61 | // definition 62 | template 63 | T MinVal(T a, T b) { 64 | return (b > a) ? a : b; 65 | } 66 | #endif 67 | ``` 68 | 69 | **Min.cc** 70 | ```cpp 71 | #include "Min.hpp" 72 | 73 | // template functions must be defined in header file 74 | ``` -------------------------------------------------------------------------------- /Lessons/Week 09/38class_templates.md: -------------------------------------------------------------------------------- 1 | # 03/23/2022: Class Templates 2 | 3 | ## Motivation 4 | - Similar to function templates, class templates of similar classes that deal with different types require entirely new classes that have mostly the exact same code 5 | - Class templates allow us to leave the types, parameters, local vars, etc. open 6 | 7 | ## Class Templates: implement Stack 8 | **Stack will be able to hold:** 9 | ```cpp 10 | Stack // doubles 11 | Stack // ints 12 | Stack // pointers to char 13 | ``` 14 | 15 | **Example stack:** 16 | ```cpp 17 | // definition 18 | #ifndef STACK_HPP 19 | #define STACK_HPP 20 | 21 | class Underflow{}; 22 | class Overflow{}; 23 | 24 | template 25 | class Stack { 26 | public: 27 | // method declaration 28 | void Push(T value); 29 | T Pop(); 30 | private: 31 | static constexpr size_t kMaxSize = 200; 32 | T v_[kMaxSize]; 33 | size_t top_ = 0; 34 | }; 35 | 36 | // definition 37 | // we also need to declare our functions as function templates 38 | // indicate member function of class and function template 39 | 40 | template 41 | void Stack::Push(T value) { 42 | if (top_ == max_size) throw Overflow(); 43 | v_[top_] = value; 44 | top_ += 1; 45 | } 46 | 47 | template 48 | T Stack::Pop() { 49 | if (top_ < 0) throw Underflow(); 50 | top -= 1; 51 | return v_[top_]; 52 | } 53 | 54 | #endif 55 | ``` 56 | 57 | To instantiate class template, **you must specify the template argument** 58 | 59 | ## Class Templates and Separate Compilation 60 | - For each template instantiation, the compiler creates its own copy of member functions for concrete type 61 | - When we do "#include" the function declarations/class definitions are in essense copy and pasted into the function 62 | - But with class templates, **again this won't be avalible till link time** 63 | - Compiler must have access to complete definition to **class template definition** and **member function template definitions** 64 | - Solution: in this class, we include definitions in .hpp header file -------------------------------------------------------------------------------- /Lessons/Week 09/39inheritance.md: -------------------------------------------------------------------------------- 1 | # 03/23/2022: Inheritance 2 | 3 | ## Motivation 4 | - Classes can model both concrete and abstract objects 5 | - **Modeling: truck -> fire truck, concrete truck** 6 | - Truck models general overall behavior 7 | - Fire truck has the same behaviors as generic truck, but also details that specifically make it a fire truck 8 | - Concrete truck has the same behaviors as generic truck, but also details that specifically make it a concrete truck 9 | 10 | You could create 3 separate classes but that would use a lot of copy/pasted code. You could create one "Super Truck" that had the behavior of all 3 trucks but that isn't efficient, giving unnecassary features to the truck. **Inheritance allows us to create a heirarchy of behavior**: 11 | - parent class 12 | - subclass 1.1, subclass 1.2 13 | - subclass 1.1.1, subclass 1.1.2, subclass 1.2.1, subclass 1.2.2 14 | 15 | Every child truck has the attributes of its parent class(es). 16 | 17 | ## Inheritance 18 | **IMPLEMENTATION INHERITANCE** 19 | - Derived classes can take advantage of existing behavior (data and member functions) in parent classes 20 | ```cpp 21 | // Forward() is a behavior of parent class Truck, but subclasses model this behavior as well 22 | // calling on object 23 | FireTruck ft; 24 | ft.Forward(); 25 | ``` 26 | **INTERFACE INHERITANCE** 27 | - Given visibility is public, the derived classes can do everything that the base class can do 28 | ```cpp 29 | // passing object by reference 30 | Firetruck ft; 31 | GotoLocation(ft); 32 | void GotoLocation(const Truck& t) { ... } 33 | ``` 34 | 35 | ## Visibility of Inheritance 36 | ``` cpp 37 | class Truck { 38 | public: 39 | // if Truck class definition is known, these can be accessed 40 | int x; 41 | protected: 42 | // only Truck and Truck children can access 43 | int y; 44 | private: 45 | // only THIS Truck can access 46 | int z; 47 | }; 48 | ``` 49 | 50 | Ex: FireTruck - **public inheritance** - "is-a" 51 | ```cpp 52 | class FireTruck : public Truck { 53 | /* 54 | FireTruck knows its parent 55 | if FireTruck is known, it is also known that FireTruck inherits from Truck 56 | -- x still public 57 | -- y still protected 58 | -- z cannot be accessed by FireTruck (private in Truck) 59 | */ 60 | } 61 | ``` 62 | 63 | Ex: FireTruck - **protected inheritance** 64 | ```cpp 65 | class FireTruck : protected Truck { 66 | /* 67 | only FireTruck, FireTruck children know they inherit from Truck 68 | -- x is protected (accessible to FireTruck/its children) 69 | -- y still protected 70 | -- z cannot be accessed by FireTruck (private in Truck) 71 | */ 72 | } 73 | ``` 74 | 75 | Ex: FireTruck - **private inheritance** - "implemented-in-terms-of" 76 | ```cpp 77 | class FireTruck : private Truck { 78 | /* 79 | only FireTruck, FireTruck children know they inherit from Truck 80 | -- x is private (accessible to FireTruck only) 81 | -- y is private (^^) 82 | -- z cannot be accessed by FireTruck (private in Truck) 83 | */ 84 | } 85 | ``` 86 | 87 | ## Inheritance, conceptually 88 | - If you do not provide a constructor/destructor in base class, compiler will create one, **called implicitly** 89 | - If we do not provide a constructor/destructor in derived class, compiler will create one 90 | - When derived constructor/destructor are called, the base class constructor/destructor are implicity called 91 | 92 | **Initializer list can initialize parent class items** 93 | ```cpp 94 | // base class constructor 95 | Parent::Parent(std::string str) : str(str) { ... } 96 | // derived class constructor 97 | Child::Child(std::string str) : Parent(str) { ... } 98 | ``` 99 | 100 | - If derived class hasn't defined a data member/function, compiler will look in parent class. 101 | - **Overloading an operator in derived class does not trigger the base class definitions.** 102 | 103 | This way parent is always resposible for its portion and child is reponsible for its portion. 104 | ```cpp 105 | Child& operator=(const Child& other) { 106 | // initialize base portion 107 | Parent::operator=(other); 108 | // initialize child portion 109 | // define 110 | return *this; 111 | } 112 | ``` 113 | 114 | ## Additional Examples 115 | - Data members of base type are constructed by base constructor, derived portion is contructed by derived constructor 116 | 117 | **BASE: TRUCK** 118 | ```cpp 119 | Truck::Truck(unsigned int weight, FuelType fuel_type, unsigned int length, unsigned int height) : weight_(weight), fuel_type_(fuel_type), length_(lenght), height_(height) { 120 | // base constructor 121 | } 122 | ``` 123 | **DERIVED: FIRETRUCK** 124 | ```cpp 125 | FireTruck::FireTruck(unsigned int weight, FuelType fuel_type, unsigned int length, unsigned int height, unsigned int water_capacity) : Truck(weight, fuel_type, length, height), water_capacity_(water_capacity) { 126 | // derived constructor 127 | // notice call to base constructor in initialization list 128 | } 129 | ``` 130 | 131 | - References/Pointers 132 | ```cpp 133 | // only base type components are stored here 134 | // shifting perspective of EXISTING object 135 | Truck& big_red_truck_base = big_red_truck; 136 | Truck* ptr_to_big_red_truck_base = &big_red_truck; 137 | 138 | PassedByReference(big_red_truck); 139 | // look at big_red_truck through lens of base 140 | 141 | // object slicing 142 | // creating a NEW object 143 | Truck red_truck = big_red_truck; 144 | 145 | PassedByValue(big_red_truck); 146 | // object slicing 147 | ``` 148 | 149 | -------------------------------------------------------------------------------- /Lessons/Week 09/40inclusion_polymorphism.md: -------------------------------------------------------------------------------- 1 | # 03/24/2022: Inclusion Polymorphism 2 | 3 | ## Name Hiding 4 | As demonstrated below, redefining behaviors within scope can be use to hide data. If no match found in inner scope, outer scope is searched. 5 | 6 | ### **Base class** 7 | ```cpp 8 | // Animal header file 9 | class Animal { 10 | public: 11 | std::string Speak(); 12 | private: 13 | }; 14 | 15 | // Animal source file 16 | std::string Animal::Speak() { 17 | return "Generic animal noise."; 18 | } 19 | ``` 20 | 21 | ### **Derived class #1** 22 | ```cpp 23 | // Parrot header file 24 | #include "animal.hpp" 25 | class Parrot : public Animal { 26 | public: 27 | std::string Speak(); 28 | private: 29 | } 30 | 31 | // Parrot source file 32 | std::string Parrot::Speak() { 33 | return "Polly wants a cracker."; 34 | } 35 | 36 | // Driver 37 | Parrot polly; 38 | polly.Speak(); 39 | ``` 40 | 41 | ### **Derived class #2** 42 | ```cpp 43 | // Parrot header file 44 | #include "animal.hpp" 45 | class Cat : public Animal { 46 | public: 47 | std::string Speak(); 48 | private: 49 | } 50 | // Parrot source file 51 | std::string Cat::Speak() { 52 | return "Meow"; 53 | } 54 | 55 | // Driver 56 | Cat bubby; 57 | bubby.Speak(); 58 | ``` 59 | 60 | ## Function Overriding and Virtual Functions 61 | ```cpp 62 | // Driver 63 | Cat bubby; 64 | Parrot polly; 65 | 66 | Animal& bubby_as_an_animal = bubby; 67 | Animal* ptr_poly_as_an_animal = &polly; 68 | 69 | std::vector animals; 70 | animals.push_back(bubby_as_an_animal); 71 | animals.push_back(ptr_poly_as_an_animal); 72 | 73 | for (auto& animal : animals) { 74 | std::cout << animal->Speak() << std::endl; 75 | } 76 | // prints "Generic animal noise." twice 77 | ``` 78 | 79 | Is there a way we can access derived class behavior when we observe a derived type object through the lens of a base type (like example above)? 80 | 81 | ### Virtual function 82 | Compiler will look for function in derived and then go up inheritance heirarchy 83 | 84 | ```cpp 85 | // Animal header file 86 | class Animal { 87 | public: 88 | virtual std::string Speak(); 89 | private: 90 | }; 91 | 92 | // Animal source file 93 | std::string Animal::Speak() { 94 | return "Generic animal noise."; 95 | } 96 | ``` 97 | 98 | Cool because collection of a base type (in this case, std::vector< Animal* >) can still access behavior of its derived objects! 99 | 100 | ## Polymorphic Behavior 101 | Regardless of underlying type, all objects will do what is appropriate for that type 102 | 103 | ## Abstract base classes and pure virtual functions 104 | - In our animal example, notice that there is not species called "animal" out there. 105 | - We created a category to group all species like cat, parrot, etc. 106 | - So it doesn't make sense for us to be able to create an object of type Animal 107 | 108 | ### How can we model this behavior? 109 | 1. **abstract** base class = Animal behaves only as a base class, cannot be created directly 110 | 2. **pure virtual functions** = all functions are made virtual and "=0" 111 | ```cpp 112 | virtual std::string Speak() = 0; 113 | 114 | // not we CANNOT create animal object, below results in compiler error 115 | Animal animal; 116 | 117 | // overrided beheavior in derived classes can still be accessed 118 | ``` 119 | 120 | ## Virtual destructors 121 | At the end of object lifetime, destructor is responsible for releasing all resources owned by that object 122 | 123 | ### Animals Created on Free Store 124 | ```cpp 125 | // Base class 126 | virtual ~Animal(); 127 | 128 | // Driver 129 | Cat* bubby = new Cat; 130 | Parrot* polly = new Parrot; 131 | 132 | Animal& bubby_as_an_animal = bubby; 133 | Animal* ptr_poly_as_an_animal = &polly; 134 | 135 | std::vector animals; 136 | animals.push_back(bubby_as_an_animal); 137 | animals.push_back(ptr_poly_as_an_animal); 138 | 139 | for (auto& animal : animals) { 140 | delete animal; 141 | } 142 | ``` -------------------------------------------------------------------------------- /Lessons/Week 10/41trees.md: -------------------------------------------------------------------------------- 1 | # 28/03/2022: Introduction to Trees 2 | 3 | ## Intro 4 | - Non-linear, hierarchial ordering 5 | - **Root**: top node of the tree 6 | - **Leaf**: node with no children 7 | - **Siblings**: children nodes of same parent 8 | - **Depth**: number of edges from node to root (top-down: root is 0 -> +1 as u go down) 9 | - **Height**: number of edges in the longest path from node to leaf 10 | - Height of leaf = 0 11 | - Height of tree = number of edges in the longest path from root to leaf (group-up: node is 0 -> +1 as u go up) 12 | - **Subtree**: tree within a tree 13 | 14 | ## Binary Trees 15 | Each node has max 2 children 16 | ```cpp 17 | // data members 18 | Node* parent; 19 | Node* left; 20 | Node* right; 21 | T data; 22 | ``` 23 | 24 | ## Trees with Unbound Branching 25 | - A tree with similar traversal as a singly linked list: go to leftmost child and traverse through right-side siblings. 26 | - Each node has an arbitrary number of children 27 | ```cpp 28 | // data members 29 | Node* parent; 30 | Node* left-child; 31 | Node* right-sibling; 32 | T data 33 | ``` 34 | 35 | ## Other Tree Representations 36 | - Trees can be stored in an array -------------------------------------------------------------------------------- /Lessons/Week 10/42bst.md: -------------------------------------------------------------------------------- 1 | # 29/03/2022: BST and BST traversal 2 | 3 | ## Intro 4 | Organized binary tree data structure 5 | - Key and data 6 | - Pointers to left, right, parent 7 | - At most 2 children 8 | - Node order satisfy 9 | - y node in left-subtree of node x: y.key < x.key 10 | - y node in right-subtree of node x: y.key >= x.key 11 | 12 | ```cpp 13 | Node* parent; 14 | Node* left; 15 | Node* right; 16 | // ex: data = student name, key = student UIN 17 | T1 data; 18 | T2 key; 19 | ``` 20 | 21 | - **Empty/Null tree**: no nodes 22 | - **Height**: button up (longest path to leaf) 23 | - **Depth**: top down 24 | - **Height-balanced BST**: abs(left subtree height - right subtree height) <= 1 25 | 26 | ## Inorder Traversal 27 | Process root of subtree BETWEEN processing values in left subtree and right subtree 28 | ```cpp 29 | void InorderTreeWalk(Node* node) { 30 | if (node != nullptr) { 31 | InorderTreeWalk(node->left); 32 | // process node 33 | InorderTreeWalk(node->right); 34 | } 35 | } 36 | ``` 37 | ![image](/Images/bst_inorder_traversal.png) 38 | - **Processing order: 4, 8, 10, 12, 14, 20, 22** 39 | 40 | ## Preorder Traversal 41 | Process root FIRST, then values in either subtree 42 | ```cpp 43 | void PreorderTreeWalk(Node* node) { 44 | if (node != nullptr) { 45 | // process node 46 | PreorderTreeWalk(node->left); 47 | PreorderTreeWalk(node->right); 48 | } 49 | } 50 | ``` 51 | ![image](/Images/bst_inorder_traversal.png) 52 | - **Processing order: 20, 8, 4, 12, 10, 14, 22** 53 | 54 | ## Postorder Traversal 55 | Process root LAST, after all values in either subtree 56 | ```cpp 57 | void PostorderTreeWalk(Node* node) { 58 | if (node != nullptr) { 59 | PreorderTreeWalk(node->left); 60 | PreorderTreeWalk(node->right); 61 | // process node 62 | } 63 | } 64 | ``` 65 | ![image](/Images/bst_inorder_traversal.png) 66 | - **Processing order: 4, 10, 14, 12, 8, 22, 20** 67 | -------------------------------------------------------------------------------- /Lessons/Week 10/43bst_queries.md: -------------------------------------------------------------------------------- 1 | # 30/03/2022: BST queries 2 | 3 | ## Minimum and Maximum 4 | - Nodes are already arranged in ordered fashion in BST 5 | - Highly efficient algorithm because: 6 | - All values lesser than parent are found in theleft subtree 7 | - All values equal to or greater than parent are found in the right subtree 8 | - We don't even have to look at the value of the node as we traverse 9 | 10 | ### **Minimum node: keep stepping left** 11 | ```cpp 12 | template 13 | auto* TreeMin(BinaryTreeNode curr) { 14 | while (curr->left != nullptr) { 15 | curr = curr->left; 16 | } 17 | return curr; 18 | } 19 | ``` 20 | 21 | ### **Maximum node: keep stepping right** 22 | ```cpp 23 | template 24 | auto* TreeMax(BinaryTreeNode curr) { 25 | while (curr->right != nullptr) { 26 | curr = curr->right; 27 | } 28 | return curr; 29 | } 30 | ``` 31 | 32 | ## Searching 33 | - Use BST ordering to make searching efficient 34 | - Choose left or right traversal based on value of current node with respect to item we are searching for 35 | 36 | ### Recursive Solution 37 | ```cpp 38 | template 39 | auto* TreeSearch(BinaryTreeNode curr, T2 key) { 40 | if (curr == nullptr || curr->key == key) { 41 | return curr; 42 | } 43 | // make a decision 44 | if (key < curr->key) { 45 | return TreeSearch(curr->left, key); 46 | } else { 47 | return TreeSearch(curr->right, key); 48 | } 49 | } 50 | ``` 51 | 52 | ### Interative Solution (rolled) 53 | ```cpp 54 | template 55 | auto* TreeSearch(BinaryTreeNode root, T2 key) { 56 | BinaryTreeNode curr = root; 57 | while (curr != nullptr && curr->key != key) { 58 | if (key < curr->key) { 59 | curr = curr->left; 60 | } else { 61 | curr = curr->right; 62 | } 63 | } 64 | return curr; 65 | } 66 | ``` 67 | 68 | ## Successor 69 | In order traversal: 4, 8, 10, 12, 20, 22 70 | ![image](/Images/bst_inorder_traversal.png) 71 | 72 | 73 | ### Successor of In Order Traversal 74 | - Successor of node x is node with smallest key greater than x->key 75 | ```cpp 76 | template 77 | auto* TreeSuccessor(BinaryTreeNode* node) { 78 | // does it have a right subtree? 79 | if (node->right != nullptr) { 80 | // if yes, you know that the next key should be the minimum value in that right subtree 81 | return TreeMinimum(node->right); 82 | } 83 | // if not, we have to go one level up and go right 84 | BinaryTreeNode* successor = node->parent; 85 | // if node is left of its parent, do not enter while loop, simply return the parent 86 | while (successor != nullptr && node == successor->right) { 87 | // if node is right of its parent we need to work our way till we point to a node is in left subtree of parent (KINDA, more compilated but we try to find the fake next right node) 88 | // complicated case bc parent cannot be successor when node is right of parent 89 | node = successor; 90 | successor = successor->parent; 91 | } 92 | return successor; 93 | } 94 | ``` 95 | 96 | ### Consider cases in this order: 97 | 1. node has right subtree 98 | 2. node does not have right subtree, and is left of its parent 99 | 3. node does not have right subtree, and is right of its parent -------------------------------------------------------------------------------- /Lessons/Week 10/44bst_construction_insertion_destruction.md: -------------------------------------------------------------------------------- 1 | # 03/31/2022: BST Construction, Insertion, Destruction 2 | 3 | ## Class setup and node configuration 4 | Node 5 | ```cpp 6 | template 7 | struct Node { 8 | Node* parent = nullptr; 9 | Node* left = nullptr; 10 | Node* right = nullptr; 11 | T1 data; 12 | T2 key; 13 | } 14 | ``` 15 | 16 | BinarySearchTree 17 | ```cpp 18 | template 19 | class BinarySearchTree { 20 | public: 21 | BinarySearchTree(); 22 | BinarySearchTree(const T1& data, const T2& key); 23 | BinarySearchTree(const BinarySearchTree& source) = delete; 24 | BinarySearchTree& operator=(const BinarySearchTree& source) = delete; 25 | ~BinarySearchTree(); 26 | void Insert(const T1& data, const T2& key); 27 | void Clear(); 28 | private: 29 | Node* root_; 30 | Clear(Node* n); 31 | }; 32 | ``` 33 | 34 | ## Construction 35 | ### Default Constructor 36 | ```cpp 37 | template 38 | BinarySearchTree::BinarySearchTree() : root_(nullptr) { 39 | // 40 | } 41 | ``` 42 | 43 | ### Parameterized Constructor 44 | ```cpp 45 | template 46 | BinarySearchTree::BinarySearchTree(const T1& data, const T2& key) : root_(nullptr) { 47 | // do not use NEW or allocated for memory in the initialization list 48 | // do that in here: 49 | auto* temp = new Node; 50 | temp->data = data; 51 | temp->key = key; 52 | root_ = temp; 53 | } 54 | ``` 55 | 56 | ## BST Insertion 57 | Similarly to successor/search, traverse tree knowing that nodes are arranged in specific order. 58 | ```cpp 59 | template 60 | void BinarySearchTree::Insert(const T1& data, const T2& key) { 61 | auto new_node = new Node; 62 | new_node->data = data; 63 | new_node->key = key; 64 | // if tree is empty 65 | if (root_ == nullptr) { 66 | root_ = new_node; 67 | return; 68 | } 69 | // similar to successor code 70 | auto* iter = root_; 71 | auto* parent = root_; 72 | while (iter) { 73 | parent = iter; 74 | if (key < iter->key) { 75 | iter = iter->left; 76 | } else { 77 | iter = iter->right; 78 | } 79 | } 80 | if (key < parent->key) { 81 | parent->left = new_node; 82 | } else { 83 | parent->right = new_node; 84 | } 85 | new_node->parent = parent; 86 | } 87 | ``` 88 | 89 | ## Destruction 90 | - Preorder traversal doesn't work because it will result in massive memory leak 91 | - Inorder traversal also doesn't work because of subtree memory leak 92 | - Postorder traversal is the ideal method of deallocation 93 | 94 | ```cpp 95 | template 96 | void BinarySearchTree::~BinarySearchTree() { 97 | Clear(); 98 | } 99 | 100 | template 101 | void BinarySearchTree::Clear() { 102 | // call overloaded definition of clear 103 | Clear(root_); 104 | root_ = nullptr; 105 | } 106 | 107 | template 108 | void BinarySearchTree::Clear(Node* n) { 109 | // perform post order traversal 110 | if (n != nullptr) { 111 | Clear(n->left); 112 | Clear(n->right); 113 | delete n; 114 | } 115 | } 116 | ``` -------------------------------------------------------------------------------- /Lessons/Week 11/45graphs.md: -------------------------------------------------------------------------------- 1 | # 04/04/2022: Graphs 2 | 3 | ## Set 4 | - Mathematically, the set is a collection of distinct objects 5 | 6 | ## Graph 7 | - **vertex** set: finite, nonempty 8 | - **edge** set may be empty, elements are 2-element subset of vertex set (edge bridges 2 vertices) 9 | 10 | ### Example 11 | ![image](/Images/graph.png) 12 | - Vertex set: {x1, x2, x3, x4, x5, x6} 13 | - Edge set: { {x1, x2}, {x1, x3}, {x2, x5}, {x3, x4} 14 | 15 | ## Graph Types 16 | - Direction 17 | - **Undirected**: edge goes both ways 18 | - **Directed**: edge must be specified in both directions to go both ways 19 | ![image](/Images/degree.jpg) 20 | - Edge 21 | - **Simple**: no two edges connect the same pair 22 | - **Multigraph**: more than 1 edge can connect same pair of vertices 23 | - **Loop**: node connected to itself 24 | - **Cycle**: circular path exists connecting back to starting node 25 | ![image](/Images/graphcycle.jpg) 26 | 27 | ## How are graphs useful 28 | - Social networks: collaboration, influence, hierarchy, etc 29 | - Transportation networks: airports, roads 30 | - Biological networks -------------------------------------------------------------------------------- /Lessons/Week 11/46more_graphs.md: -------------------------------------------------------------------------------- 1 | # 04/05/2022: Graph Representations 2 | 3 | ## Terminology 4 | - **Connected**: there is a path from every vertex to every other vertex in the graph 5 | - **Not connected**: set of connected components, there are vertices that cannot be reaches in a path 6 | - **Acyclic**: graph with no cycle, ex: TREE!! 7 | - **Path**: sequence of vertices that are visited 8 | - **Length** of path or cycle: number of edges visited 9 | - **Density**: proportion of possible pairs of vertices that are connected by edges 10 | - Sparse: few possible edges present 11 | - Dense: few possible edges missing 12 | - **Adjacent**: 2 nodes are adjacent if an edge exists between them 13 | 14 | ## Vertex Naming & Edge Notation 15 | - Undirected graph 16 | - 0 through V-1 for vertices 17 | - v-w connected edge between node v and w, in this case w-v refers to the same edge 18 | 19 | ## Graph Representation Alternatives 20 | The following representations both have **space** and **time** tradeoffs. 21 | 22 | ### Adjacency-Matrix representation 23 | ![img](/Images/graphmatrix.png) 24 | - row = source, col = destination 25 | - colored values show us that it is undirected (every edge goes both ways) 26 | - val = 1 if (i, j) in E, else 0 27 | - **0(V^2) memory**, independent of # of edges in graph G 28 | - preferred when |E| is closer to |V|^2 29 | - uses more memory, but efficient to look up node edge connections 30 | 31 | ### Adjacency-List representation 32 | ![img](/Images/graphlist.png) 33 | - list contains pointers to linked list 34 | - index = source, list = connected to 35 | - uses less memory, efficient to search list to determine edge edge connections 36 | 37 | ## Etc. 38 | - Matrix representation is inefficient for sparce graph because u are just taking memory for a bunch of 0's, list is more efficient for that 39 | ![img](/Images/graphrep.png) 40 | -------------------------------------------------------------------------------- /Lessons/Week 11/47implementing_undirected_graph_using_adjacency_lists.md: -------------------------------------------------------------------------------- 1 | # 04/06/2022: Implementing an undirected graph type using adjacency lists 2 | 3 | ## What we are implementing 4 | ```cpp 5 | template 6 | class SimpleUndirectedGraph { 7 | public: 8 | SimpleUndirectedGraph(size_t no_v); 9 | void AddEdge(size_t v, size_t, w); 10 | bool IdAdjacent(size_t v, size_t w); 11 | 12 | std::pair::const_iterator, std::list::const_iterator> Asj(size_t v) const; 13 | size_t NoVerticies() const { return no_verticies_; } 14 | size_t NoEdges() const { return no_edges_; } 15 | 16 | template 17 | friend std::ostream& operator<<(std::ostream& os, const SimpleUndirectedGraph& sug); 18 | 19 | private: 20 | std::vector verticies_; 21 | std::vector> adj_; 22 | size_t no_verticies_; 23 | size_t no_edges_; 24 | }; 25 | ``` 26 | 27 | ## Construction 28 | ```cpp 29 | template 30 | SimpleUndirectedGraph::SimpleUndirectedGraph(size_t no_v) : no_verticies_(no_v), no_edges_(0) { 31 | verticies_.resize(no_v); 32 | adj_.resize(no_v); 33 | } 34 | ``` 35 | 36 | ## Adding edges 37 | ```cpp 38 | template 39 | void SimpleUndirectedGraph::AddEdge(size_t v, size_t w) { 40 | if (IsAdjacent(v, w) || IsAdjacent(w, v)) { 41 | throw std::invalid_argument("edge already exists in graph"); 42 | } 43 | adj_.at(v).push_back(w); 44 | adj_.at(w).push_back(v); 45 | no_edges += 1;() 46 | } 47 | ``` 48 | 49 | ## Are we neighbors 50 | ```cpp 51 | template 52 | bool SimpleUndirectedGraph::IsAdjacent(size_t v, size_t w) { 53 | // use (find) function from standard libary list 54 | return (std::find(adj_.at(v).cbegin(), adj_.at(v).cend(), w) != adj_.at(v).cend()); 55 | } 56 | ``` 57 | 58 | ### Iterators 59 | - Some way to move through data similarly to how we did in an array 60 | - Sequence of nodes 61 | 62 | ## Who are our neighbors 63 | ```cpp 64 | template 65 | std::paid::const_iterator, std::list::const_iterator> SimpleUndirectedGraph::Adj(size_t v) const { 66 | return std::make_pair(adj_.at(v).cbegin(), adj_.at(v).cend()); 67 | } 68 | ``` -------------------------------------------------------------------------------- /Lessons/Week 11/48DFS.md: -------------------------------------------------------------------------------- 1 | # 04/07/2022: Depth-First Search (DFS) 2 | 3 | ## Analogy: Search in Maze 4 | - pick any passage you can go down, but unroll a string on your way there so you can find your way back 5 | - mark all intersections and passages when you visit them so you know when you arrive at them again 6 | - retrace steps till you find a new passage to take 7 | 8 | ## DFS intro 9 | - recursive call 10 | - if we encouter edge v-u 11 | - or skip the edge if u is marked as visited 12 | - once all edges incident to vertex v are processed, we "return" to continue exploration from vertex w that led us to v 13 | 14 | ## DFS walkthrough 15 | - maintain a marked list to keep track of visited 16 | - as you traverse, consult adjacency list 17 | - if all nodes in adjacency list are visited, it will return from recursive call 18 | 19 | ## HW: iterative dfs on graph with no helpers, works on cyclic graphs too 20 | ```cpp 21 | std::vector TrivialGraph::DepthFirstTraversal(const std::string& origin) { 22 | std::vector visited; 23 | std::stack stack; 24 | std::set stack_elems; 25 | stack.push(origin); 26 | stack_elems.insert(origin); 27 | while (!stack.empty()) { 28 | std::string str = stack.top(); 29 | stack.pop(); 30 | // stack_elems.erase(str); 31 | bool in_vect = false; 32 | for (size_t i = 0; i < visited.size(); i++) { 33 | if (visited.at(i) == str) { 34 | in_vect = true; 35 | } 36 | } 37 | if (!in_vect) { 38 | visited.push_back(str); 39 | std::list adj_list = graph_.at(str); 40 | for (std::string curr : adj_list) { 41 | bool in_stack = false; 42 | if (stack_elems.find(curr) != stack_elems.end()) { 43 | in_stack = true; 44 | } 45 | if (!in_stack) { 46 | std::cout << curr << std::endl; 47 | stack.push(curr); 48 | stack_elems.insert(curr); 49 | } 50 | } 51 | } 52 | } 53 | return visited; 54 | } 55 | ``` -------------------------------------------------------------------------------- /Lessons/Week 12/49move_semantics.md: -------------------------------------------------------------------------------- 1 | # 04/11/22: Introduction to Move Semantics 2 | 3 | ## Move Semantics 4 | - Special member classes in C++98 (1998) require 5 | - Default constructor 6 | - Destructor, copy constructor, copy assignment operator (if you do one, you do all 3) 7 | - Special member classes in C++11 (2011) require 8 | - Previous 9 | - PLUS move constructor 10 | - PLUS move assignment operator 11 | 12 | ```cpp 13 | // Move Constructor 14 | Type(Type&& rhs); 15 | // Move Assignment Operator 16 | Type& operator=(Type& rhs); 17 | ``` 18 | 19 | - Rule of 3 -> **Rule of 5**: if you implement one, you must implement all five 20 | - **Move Semantics**: special member functions that make it possible for compilers to replace expensive copy operations with less expensive moves 21 | 22 | ## Type&& 23 | 1. rvalue reference, which only binds to rvalues, used to identify objects that moved 24 | 2. universal reference which can mean either rvalue reference or an lvalue reference. dual nature allows it to bind to both r- and l- values 25 | 26 | ### lvalue 27 | - Represents an object that occupies an identifiable location in memory 28 | - These can be modified 29 | ```cpp 30 | int i = 0; 31 | i = 4; 32 | ``` 33 | 34 | ### rvalue 35 | - Defined by exclusion, every expression is either an lvalue or rvalue and that an rvalue is NOT an lvalue 36 | ```cpp 37 | int i = 0; 38 | (i % 2) = 1; 39 | // resulting value of i % 2 is TEMP, thus rvalue 40 | ``` 41 | 42 | ## Consider Object Ownership... 43 | - Transfer contents of objects being transfered, ex: nodes of list_a transferred to list_b 44 | 1. Create shallow copy: move addresses over 45 | 2. Terminate sharred resource, point to nullptr 46 | 47 | ## Transferring Ownership with std::move() 48 | Ex 1 49 | ```cpp 50 | std::vector v1{1, 2, 3}; 51 | std::vector v2 = std::move(v1); 52 | // this calls vector's move constructor 53 | // the std::move call becomes std::vector&&, like below 54 | // std::vector move(std::vector&& rhs) 55 | ``` 56 | 57 | Ex 2 58 | ```cpp 59 | std::vector v1{1, 2, 3}; 60 | std::vector v2{4, 5, 6}; 61 | v2 = std::move(v1); 62 | // first check self assignment 63 | // free v2 memory, delete is called 64 | // content/resource is sharred 65 | // (ownership is transferred) AKA terminate sharing 66 | ``` 67 | 68 | ## Move Semantics for Doubly Linked List 69 | ```cpp 70 | template 71 | DoublyLinekdList::DoublyLinkedList(DoublyLinkedList&& rhs) : head_(rhs.head_), tail_(rhs.tail_), size_(rhs.size_) { 72 | // initializer list performs shallow copy 73 | // resources are sharred 74 | // lets break sharred resources: 75 | rhs.head_ = nullptr; 76 | rhs.tail_ = nullptr; 77 | rhs.size_ = 0; 78 | // move complete 79 | } 80 | 81 | template 82 | DoublyLinkedList& DoublyLinkedList::operator=(DoublyLinkedList&& rhs) { 83 | if (this == &rhs) { 84 | return *this; 85 | } 86 | clear(); 87 | head_ = rhs.head_; 88 | rhs.head_ = nullptr; 89 | tail_ = rhs.tail_; 90 | rhs.tail_ = nullptr; 91 | size_ = rhs.size_; 92 | rhs.size_ = 0; 93 | return *this; 94 | } 95 | ``` 96 | 97 | ## Summary, etc. 98 | ![image](/Images/move_summary.png) 99 | 100 | Compiler implicitly replaces expensive copy operations with less expensive moves. -------------------------------------------------------------------------------- /Lessons/Week 12/50smart_pointers.md: -------------------------------------------------------------------------------- 1 | # 04/12/22: Introduction to Smart Pointers 2 | 3 | ## Cons with raw pointers 4 | - Declaration doesn't mention whether it points to single object, array, etc. 5 | - Declaration doesn't reveal whether there is any memory to be deallocated/object to be deleted 6 | - If you determine that you need to destroy some memory, how should this deallocation be done? Ex: delete, delete[], or defined delete function for obejct? 7 | - Hard to tell when we are left with dangling pointers 8 | - Incorrect deletion can lead to memory leaks, etc. 9 | 10 | ## Smart Pointers 11 | - Behavior is similar to raw pointers, but they also safegaurd against the pitfalls 12 | ```cpp 13 | #include 14 | 15 | std::unique_ptr 16 | 17 | std::shared_ptr 18 | 19 | std::weak_ptr 20 | 21 | std::auto_ptr // only use if u need to compile code with the 1998 compiler (C++98) 22 | ``` 23 | 24 | ## std::unique_ptr 25 | - Used for **exclusive-ownership resource management** 26 | - Acquire resource in constructor, release in destructor 27 | - **Copy semantics are disabled** (NO COPY CONSTRUTOR, NO COPY ASSIGNMENT OPERATOR), no opportunity to share resource 28 | - Move semantics tranfer ownership from dynamically allocated object to another, since no copies. 29 | 30 | ```cpp 31 | std::unique_ptr ptr = make_unique(7); 32 | ``` 33 | 34 | ## std::shared_ptr 35 | - Used for **shared-ownership resource management** 36 | - No specific shared_ptr will own the object, the last shared_ptr object standing destroys the object pointed to 37 | - **Copy semantics are defined** to allow shared behavior 38 | - **CONTROL BLOCK**: keeps track of how many pointers point to this object (reference count), once pointers are deleted, object moment is automatically deallocated 39 | 40 | ```cpp 41 | std::shared_ptr ptr = make_shared(7); 42 | auto ptr2 = ptr; 43 | auto ptr3 = ptr; 44 | ``` 45 | 46 | ## std::weak_ptr 47 | - Smart pointer but acts like shared, but doesn't participate in shared ownership 48 | - Useful for cyclic relationships with clear hierarchy 49 | - Doesn't maintain reference count 50 | - **This pointer knows when it dangles** 51 | - Usually created from std::shared_ptr 52 | - Doesn't have a deference operation 53 | - Not used in this class much... 54 | 55 | ## std::make_unique, std::make_shared 56 | - make_unique used to initialize unique pointer objects (at least C++14) 57 | - make_shared used to initialize shared pointers (at least C++11) -------------------------------------------------------------------------------- /Lessons/Week 12/51unique_pointers.md: -------------------------------------------------------------------------------- 1 | # 04/13/22: Introduction to Unique Pointers 2 | 3 | ## RAII and destructors revisited 4 | - RAII is a technique for resource mangement based on scope 5 | - Order for destructor calls: 6 | - Class destructor is called and body of destructor function is executed 7 | - Destructors for nonstatic member objects is called 8 | - Destructors for non-virtual base classes is called 9 | - Destructors for virtual base classes is called 10 | 11 | ## Applying unique pointer to data structures 12 | 13 | Vector should be made using unique pointer 14 | ```cpp 15 | array = std::make_unique(capacity); 16 | ``` 17 | 18 | SinglyLinkedList should be made using unique pointer 19 | ```cpp 20 | head_ = std::unique_ptr> 21 | ``` 22 | 23 | DoublyLinkedList should be made using unique pointer 24 | ```cpp 25 | head_ = unique_ptr> 26 | // node 27 | next_ = unique_ptr 28 | prev = raw pointer 29 | ``` 30 | 31 | BinarySearchTree should be made using unique pointer 32 | ```cpp 33 | root = std::unique_ptr> 34 | // node 35 | parent = Node* 36 | left = std::unique_ptr> 37 | right = std::unique_ptr> 38 | ``` 39 | 40 | ## Behaviors of unique pointers 41 | - **Parameterized constructor**: constructors new unique ptr object to passed resource 42 | - **Destructor**: releases recourses currently held 43 | - **Move assignment operator**: transfered ownership 44 | - **Release**: releases ownership of resource to caller, who then is responsible for eventually deleting it 45 | - **Reset**: releases ownership of resource currently owned and takes ownership of object whose address is passed 46 | - **Swap**: swaps owned resources with passed argument unique pointer object 47 | - **Get**: returns raw pointer to owned resource 48 | - **operator bool**: checks whether unique ptr owns resource, i.e. get() != nullptr 49 | - **operator* and operator->**: returns resource owned by unique ptr 50 | - **operator[]**: provides indexed access to resources -------------------------------------------------------------------------------- /Lessons/Week 12/52shared_pointers.md: -------------------------------------------------------------------------------- 1 | # 04/14/22: Introduction to Shared Pointers 2 | 3 | ## unique_ptr vs shared_ptr 4 | - No shared pointer owns the object 5 | - The last std::shared_ptr object standing will destroy the object being pointed to 6 | - Parts of the **Control Block** 7 | - Reference Count 8 | - Weak Count 9 | - Other data (custom deleter, allocator, etc.) 10 | - [Documentation](https://en.cppreference.com/w/cpp/memory/shared_ptr) 11 | 12 | ## Implement sharred_ptr 13 | ```cpp 14 | template 15 | class SharedPointer { 16 | public: 17 | SharedPointer(T* address_shared_object); 18 | SharedPointer(const SharedPointer& source); 19 | SharedPointer& operator=(const SharedPointed& source) = delete; // if you like, you can define but the reason for this is so you cannot set one shared_ptr to another 20 | ~SharedPointer(); 21 | T* Get() { return holder_; } 22 | T& operator*() { return *holder_; } 23 | size_t UseCount() { return *reference_count_; } 24 | bool Unique() { return *reference_count_ == 1; } 25 | private: 26 | T* holder_; 27 | size_t* reference_count_; 28 | } 29 | 30 | // constructor 31 | template 32 | SharedPointed::SharedPointer(T* address_shared_object) : holder_(address_shared_object), reference_count_(nullptr) { 33 | reference_count_ = new size_t(1); 34 | } 35 | 36 | // copy constructor 37 | template 38 | SharedPointer::SharedPointer(const SharedPointer& source) : holder_(source.holder_), reference_count_(source.reference_count_) { 39 | (*reference_count_) += 1; 40 | } 41 | // destructor 42 | template 43 | SharedPointer::~SharedPointer() { 44 | (*reference_count_) -= 1; 45 | if ((*reference_count_) == 0) { 46 | delete holder_; 47 | delete reference_count_; 48 | } 49 | } 50 | ``` -------------------------------------------------------------------------------- /Lessons/Week 14/53design_patterns.md: -------------------------------------------------------------------------------- 1 | # 04/18/22: Introduction to Design Patterns 2 | 3 | ## Design Patterns 4 | - Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice 5 | 6 | ### Parts of the design pattern: 7 | - **Name**: describes problem, solution, consequences in a single word or two. allows us to develop vocab to discuss with peers, documentation, etc. 8 | - **Problem solved**: when to apply, problem + context 9 | - **Solution**: abstract description of pattern, should be adaprted to different situations 10 | - **Consequences**: trade-offs 11 | 12 | ## Micro Patterns 13 | ### Most-wanted holder 14 | - **Name**: most-wanted holder 15 | - **Problem**: what to find the "most wanted" element of a collection 16 | - **Solution**: 17 | - initialize most-wanted holder to the first element in the collection 18 | - compare every other element to value in most-wanted holder 19 | - update if new value is better 20 | ```cpp 21 | std::vector things; 22 | Thing most_wanted = things.at(0); 23 | for (size_t i = 0; i < things.size(); i++) { 24 | if (things.at(i) > most_wanted) { 25 | most_wanted = things.at(i); 26 | } 27 | } 28 | ``` 29 | 30 | ### One-way flag 31 | - **Name**: One-way flag 32 | - **Problem**: want to know if a property is true/false for every element in a collection 33 | - **Solution**: 34 | - initialize a boolean to one value 35 | - traverse the whole collection 36 | - setting the boolean to the other value if an element violates the property 37 | ```cpp 38 | std::vector things; 39 | bool all_valid = true; 40 | for (auto thing : things) { 41 | if (!thing.Value()) { 42 | all_valid = false; 43 | break; 44 | } 45 | } 46 | ``` 47 | 48 | ### Follower 49 | - **Name**: Follower 50 | - **Problem**: want to compare adjavent elements of collection 51 | - **Solution**: 52 | - iterate through a collection 53 | - set the value of some follower variable to the current element as the last step 54 | ```cpp 55 | std::vector things; 56 | Thing follower = things.at(0); 57 | bool collection_in_order = true; 58 | for (auto thing : things) { 59 | // note first comparison will be a self comparison in this implementation 60 | if (follower > thing) { 61 | collection_in_order = false; 62 | } 63 | follower = thing; 64 | } 65 | ``` 66 | 67 | ## Strategy Pattern 68 | - Intent: define a family of algorithms, encapsulate each one, make them interchangable 69 | - Use when: 70 | - Many related classes differ only in their behavior 71 | - You need different variants of an algorithm 72 | - An algorithm uses data that clients shouldn't know about 73 | - Encapsulate the algorithm data from the client 74 | - Class defines multiple behaviors and these are implemented using conditionals 75 | 76 | ![Image](/Images/strategy_pattern.png) 77 | 78 | Ex: Bird class has Speak() function that is implemented by every child class because each bird makes a unique sound. But what if some birds make the same sound? (Rubber Duck makes no quack, Mallard Duck makes loud quack...) **Turn this behavior into its own abstract class: HAS-A behavior** 79 | 80 | ![Image](/Images/strategy_pattern2.png) 81 | -------------------------------------------------------------------------------- /Lessons/Week 14/54iterators.md: -------------------------------------------------------------------------------- 1 | # 04/19/22: Introduction to Iterators 2 | 3 | ## Motivation 4 | - **Container** is an object that holds other objects: lists, vectors, trees, graphs, etc. 5 | - They can have a sequence, ex: beginning = head, end = tail 6 | - Common **Algorithms**: tasks performed on containers 7 | - Collect data into container 8 | - Organize data by some rule 9 | - Retrieve items (by index, by value, by property, etc) 10 | - Add new items 11 | - Remove existing items 12 | - Search 13 | - Sort 14 | - Numeric operations (sum all items, etc) 15 | - A lot of the logic for tasks such as searching have the same logic, is a generic solution possible? 16 | 17 | ## Iterators 18 | - **Iterator is an abstraction of a pointer to some element in a sequence** 19 | - Operators of iterator 20 | - **operator\*** - access the element we are currently visiting 21 | - **operator++** - move to the next element in sequence 22 | - **operator==** - check if two iterators are visiting same element 23 | 24 | ## Simplified std::vector iterator 25 | ```cpp 26 | template 27 | InputIt Find(InputIt first, InputIt last, const T& value) { 28 | while (first != last) { 29 | if (*first == value) return first; 30 | first++; 31 | } 32 | return last; 33 | } 34 | ``` 35 | 36 | ## STL overview 37 | - Algorithms manipulate data, but don't know about containers. Containers store data, but don't know about algorithms. **Iterators bridge this gap** while providing 38 | - Uniform access to data 39 | - Independent of how it is stored 40 | - Independent of type 41 | - Easy/fast to read/modify 42 | - Read/White iterators: .begin(), .end() 43 | - Read only iterators: .cbegin(), cend() 44 | 45 | ### STL Iterators in action 46 | ```cpp 47 | std:vector v{1, 2, 3, 4}; 48 | for (auto iter = v.begin(); iter != v.end(); iter++) { 49 | std::cout << *iter << std::endl; 50 | } 51 | 52 | auto iter_to_result = std::find(v.begin(), v.end(), 2); 53 | if (iter_to_result != v.end()) { 54 | std::cout << *iter_to_result << std::endl; 55 | } else { 56 | std::cout << "elem not found" << std::endl; 57 | } 58 | ``` -------------------------------------------------------------------------------- /Lessons/Week 14/55doubly_linked_list_iterator.md: -------------------------------------------------------------------------------- 1 | # 04/20/22: Implementing Doubly Linked List Iterator 2 | 3 | ## Set-up 4 | - current_ : Node<\T>* 5 | - DoublyLinkedListIterator() 6 | - DoublyLinkedListIterator(Node<\T>* ptr) 7 | - operator*() const : T& 8 | - operator++() : DoublyLinkedListIterator<\T>& 9 | - operator++(int) : DoublyLinkedListIterator<\T> 10 | - operator!=(const DoublyLinkedListIterator<\T> & other) const : bool 11 | - operator==(const DoublyLinkedListIterator<\T>& other) const : bool 12 | 13 | ## Implementation 14 | ```cpp 15 | template 16 | // TELL THE COMPILER THIS CLASS HAS IMPLEMENTED ITERATOR USING COLON 17 | class DoublyLinkedListIterator: public std::iterator { 18 | public: 19 | DoublyLinkedListIterator(): current_(nullptr){} 20 | DoublyLinkedListIterator(Node* ptr) { current_ = ptr; } 21 | T& operator*() const { return current_->data; } 22 | bool operator==(const DoublyLinkedListIterator& other) const { return (current_ == other.current_); } 23 | bool operator!=(const DoublyLinkedListIterator & other) const { return !(*this == other); } 24 | // iterator operators 25 | DoublyLinkedListIterator& operator++(); 26 | DoublyLinkedListIterator operator++(int); 27 | private: 28 | Node* current_; 29 | } 30 | 31 | // pre-increment 32 | // increment, return incremented value 33 | template 34 | DoublyLinkedList& DoublyLinkedListIterator::operator++() { 35 | if (current_) { 36 | current_ = current_->next; 37 | } 38 | return *this; 39 | } 40 | 41 | // post-increment 42 | // store current value, increment, return current 43 | DoublyLinkedList& DoublyLinkedListIterator::operator++(int) { 44 | auto clone(*this); 45 | if (current_) { 46 | current_ = current_->next; 47 | } 48 | return clone; 49 | } 50 | ``` 51 | 52 | ## Update DoublyLinkedList<\T> to use iterator 53 | ```cpp 54 | class DoublyLinkedList { 55 | public: 56 | // ... 57 | using iterator = DoublyLinkedListIterator; 58 | iterator begin() { return DoublyLinkedListIterator(head_); } 59 | iterator end() { return DoublyLinkedListIterator(nullptr); } 60 | private: 61 | Node* head_ = nullptr; 62 | Node* tail_ = nullptr; 63 | size_t size_ = 0; 64 | } 65 | ``` 66 | 67 | ## Use 68 | - Without implementing iterators for containers, we cannot used range-based enhanced for-loop 69 | - After implementation: 70 | ```cpp 71 | DoublyLinkedList list{std::vector{10,20,30}}; 72 | for (auto val : list) { 73 | std::cout << val <::iterator i = list.begin(); i != list.end(); ++i) { 82 | std::cout << *i << std::endl; 83 | } 84 | 85 | /* prints: 86 | 10 87 | 20 88 | 30 */ 89 | ``` -------------------------------------------------------------------------------- /Lessons/Week 14/56binary_tree_iterator.md: -------------------------------------------------------------------------------- 1 | # 04/21/22: Implementing Binary Tree Iterator 2 | This implementation will perform an inorder traversal for a Binary Search Tree 3 | 4 | ## Implementation 5 | ```cpp 6 | template 7 | class BinarySearchTreeIterator: public std::iterator { 8 | public: 9 | BinarySearchTreeIterator(Node* iter_init); 10 | BinarySearchTreeIterator& operator++(); 11 | T1& operator*() const { return iter_->data; } 12 | bool operator==(const BinarySearchTreeIterator& other) { return (iter_ == other.iter_); } 13 | bool operator!=(const BinarySearchTreeIterator& other) { return !(iter_ == other.iter_); } 14 | 15 | private: 16 | Node* iter_; 17 | std::stack*> stack_; 18 | } 19 | 20 | template 21 | BinarySearchTreeIterator::BinarySearchTreeIterator(Node* iter_init): iter_(iter_init) { 22 | if (iter_ == nullptr) { 23 | return; 24 | } 25 | while (iter_ != nullptr) { 26 | stack_.push(iter_); 27 | iter_ = iter_->left; 28 | } 29 | iter_ = stack_.top(); 30 | stack_.pop(); 31 | } 32 | ``` 33 | At this point, we have a node pointer to the first element in the BST 34 | 35 | ## "next" Operation 36 | ![Image](/Images/BST_iterator.png) 37 | ```cpp 38 | template 39 | BinarySearchTreeIterator& BinarySearchTreeIterator::operator++() { 40 | if (!stack.empty()) { 41 | iter_ = stack_.top(); 42 | stack_.pop(); 43 | // now we need to set this up 44 | // in a way such that the next of 8 is 10 45 | // not 20 or the other nodes 46 | if (iter_->right != nullptr) { 47 | auto* curr = iter_->right; 48 | while (curr != nullptr) { 49 | stack_.push(curr); 50 | curr = curr->left; 51 | } 52 | } 53 | } else { 54 | iter_ = nullptr; 55 | } 56 | return *this; 57 | } 58 | ``` 59 | 60 | ## Changes to BST class 61 | ```cpp 62 | template 63 | class BinarySearchTree { 64 | public: 65 | // ... 66 | using iterator = BinarySearchTreeIterator; 67 | iterator begin() { return BinarySearchTreeIterator(root_); } 68 | iterator end() { return BinarySearchTreeIterator(nullptr); } 69 | private: 70 | // ... 71 | } 72 | ``` -------------------------------------------------------------------------------- /Lessons/Week 15/57programming_paradigms.md: -------------------------------------------------------------------------------- 1 | # 04/25/22: Programming paradigms 2 | 3 | - Programming Paradigm: style of programming 4 | 5 | ### Imperative programming 6 | - Control flow is **explicit** 7 | - Commands show how the computation must be done 8 | 9 | ## Declarative programming 10 | - Control flow is **implicit** 11 | - Commands focused on getting result, not on how to obtain it 12 | 13 | ## Functional programming 14 | - Control flow expressed as **combination of functions** 15 | - Use conditional expressions and recursion to perform computation 16 | 17 | ## Programming paradigms in this class 18 | - **Imperative programming**: writing series of statements to show the computer how to solve a problem 19 | - **Procedural programming**: imperative but with a procedure, i.e. function calls 20 | - **Object-oriented programming**: using objects to encapsulate program state (data) together with behavior (algorithms) 21 | - **Recursive programming**: solving problems by breaking them into smaller pieces 22 | - **Generic programming**: writing code that works with a variety of types, presented as arguments, i.e. function templates <..T..> 23 | 24 | ## Beyond C++ 25 | - Programming paradigms transcend C++ 26 | 27 | ## Progression in this Course 28 | We went from purely imperative, to object-oriented, to recursive. All these concepts were once unfamiliar, but now feel more natural with practice. -------------------------------------------------------------------------------- /Lessons/Week 15/58functional_style_programming.md: -------------------------------------------------------------------------------- 1 | # 04/26/22: Functional-Style Programming 2 | 3 | ## Terminology 4 | - **Higher-order functions**: function that operates or uses another function 5 | - **First class functions**: functions can be stored as variables 6 | ```cpp 7 | std::function filter; 8 | 9 | ... 10 | 11 | if (filter(movie)) { 12 | filtered_movies.push_back(movie); 13 | } 14 | ``` 15 | 16 | ## Functional-style programming 17 | - Solve problems by composing functions rather than loops 18 | - Reusable higher-order functions 19 | ```cpp 20 | std::vector filtered_movies; 21 | std::copy_if(movies.begin(), 22 | movies.end(), 23 | std::back_inserter(filtered_movies), 24 | [](auto& m) { return m.release_year < 1990; }); 25 | ``` 26 | 27 | - Strict funtional languages: Haskell, OCaml -------------------------------------------------------------------------------- /Lessons/Week 15/59function_pointers_and_function_objects.md: -------------------------------------------------------------------------------- 1 | # 04/27/22: Function Pointers and Function Objects 2 | 3 | ## Set-up 4 | ```cpp 5 | enum class Rating { kR, kPG, kPG13, kR}; 6 | struct Movie { 7 | std::string title; 8 | std::string director; 9 | Rating rating; 10 | unsigned int release_year; 11 | } 12 | ``` 13 | 14 | Declarative approach seems faster than imperitive, how can we move forward from here? 15 | 16 | ## Function pointers 17 | - Printing a function without the call operator prints an ADDRESS!! So we can make a pointer to this address of the function 18 | ```cpp 19 | // function_return_type (*PointerName)(function_parameter_list_types) = initializer; 20 | 21 | bool (*MySillyFunctionPtr)(int, int) = MySillyFunction; 22 | 23 | MySillyFunctionPtr(1, 2); // works the same as MySillyFunction(1, 2) 24 | ``` 25 | - **Advantage**: minimal syntactic overhead 26 | - **Disadvantage**: doesn't retain state within a scope 27 | 28 | ## Function objects 29 | - Any type that implements the call operator (operator()) 30 | - This creates callable objects 31 | - **Advantage**: doesn't retain state within a scope 32 | - **Disadvantage**: minimal syntactic overhead 33 | 34 | ```cpp 35 | class ReleasedBefore { 36 | public: 37 | ReleasedBefore(unsigned int year): year_(year) { } 38 | bool operator()(const Movie& movie) const { return movie.release_year < year_; } 39 | private: 40 | unsigned int year_; 41 | } 42 | ``` 43 | 44 | ## Defining higher-order functions that accept function pointers AND function objects 45 | ```cpp 46 | std::vector FilterMovies(const std::vector& movies, std::function filter) { 47 | std::vector filtered_movies; 48 | for (auto& movie : movies) { 49 | if (filter(movie)) { 50 | filtered_movies.push_back(movie); 51 | } 52 | } 53 | return filtered_movies; 54 | } 55 | ``` -------------------------------------------------------------------------------- /Lessons/Week 15/60lambda_functions.md: -------------------------------------------------------------------------------- 1 | # 04/28/22: Lambda Functions 2 | 3 | ## Intro 4 | - An **anonymous function** is a function definition that is not bound to an ifentifier. 5 | - Anonymous functions are often arguments being passed to higher-order functions or used for constructing the result of higher-order functions that need to return a function. 6 | - Lambda expressions = easy to create function objects 7 | - Doesn't really add much new value 8 | - Just syntactical sugar 9 | - Lambda expressions is just an expressions 10 | - **Closure** is the runtime object created by a lambda 11 | - It can hold **copies of** or **references to** the captured data 12 | 13 | ## Parts of a lambda expression 14 | ```cpp 15 | [](...) mutable -> bool { do something, return true; } 16 | // capture clause: lambda introducer 17 | // parameter list (optional): lambda declarator 18 | // mutable specification (optional) 19 | // trailing return type (optional) 20 | // lambda body 21 | ``` 22 | 23 | ### Closure clause 24 | - Captured by reference: prefix & 25 | - Captured by value: no prefix & 26 | - Empty capture clause[] means lambda doesn't receive any variables from surrounding scope 27 | - [ & ] means all variables referred to are captured by reference 28 | - [ = ] means all variables referred to are captured by value 29 | 30 | ### Parameter list 31 | - Specify types and identifiers 32 | 33 | ### Mutable specification 34 | - By default, captured by value variables are immutable 35 | - By writing mutable, captured by value variables are now mutable and **you can assign to them** 36 | 37 | ### Return type 38 | - Often inferred so left empty 39 | - But can specify explicitly 40 | 41 | ### Lambda body 42 | - Can contain anything an ordinary function contains 43 | - Captured vars 44 | - Parameters 45 | - Class data members 46 | - Statis storage (global vars) 47 | 48 | ## Lambda expressions as syntactic sugar for function objects 49 | - Closure is instantiated from closure class 50 | - Statements inside a lambda become executable instructions in the member functions 51 | 52 | ### Empty closure clause (no capturing) 53 | ```cpp 54 | // lambda 55 | auto lambda = [](const Movie& m) { return m.release_year < 1990; }; 56 | 57 | // equivalent code 58 | class Functor { 59 | public: 60 | bool operator()(const Movie& m) const { return m.release_year < 1990; } 61 | }; 62 | ``` 63 | 64 | ### Capture by value 65 | DATA MEMBER WILL BE CONSTANT!!! CANNOT DO (YEAR += 1) FOR EXAMPLE UNLESS WE MARK AS **MUTABLE**. 66 | ```cpp 67 | // lambda 68 | unsigned int year = 1950; 69 | auto lambda = [year](const Movie& m) { return m.release_year < 1950; }; 70 | 71 | // equivalent code 72 | class Functor { 73 | public: 74 | Functor(unsigned int year) : year_(year) {} 75 | bool operator()(const Movie& m) const { m.release_year < year; } 76 | private: 77 | unsigned int year_; 78 | } 79 | ``` 80 | 81 | ### Capture by reference 82 | OBJECT CAPTURED CAN BE MODIFIED THE SAME WAY AS A PARAMETER PASSED BY REFERENCE! 83 | ```cpp 84 | // lambda 85 | unsigned int year = 1950; 86 | auto lambda = [&year](const Movie& m) { return m.release_year < year; } 87 | // equivalent code 88 | class Functor { 89 | public: 90 | Functor(unsigned int& year) : year_(year) { } 91 | bool operator()(const Movie& m) const { return m.release_year < year_; } 92 | private: 93 | unsigned int& year_; 94 | } 95 | ``` -------------------------------------------------------------------------------- /Lessons/compiler.md: -------------------------------------------------------------------------------- 1 | ## to run code 2 | 3 | ``` 4 | clang++-10 -std=c++20 -Wall -Wextra -Werror -pedantic -Iincludes ./src/driver.cc ./includes/XXX.hpp ./src/XXX.cc obj/XXX.o 5 | ``` 6 | 7 | ``` 8 | ./a.out 9 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CS128-Notes --------------------------------------------------------------------------------