├── .gitignore ├── Chapter1 ├── basic_structure.md ├── control_statements.md ├── functions.md ├── introduction.md ├── operators_expressions.md └── variables.md ├── Chapter2 ├── cmake_intro.md ├── data_structures.md ├── platformio.md ├── standard_library.md └── version_control.md ├── Chapter3 ├── classes.md ├── io_streams.md ├── raii.md └── types_refs_ptrs.md ├── Chapter4 ├── memory.md ├── move.md ├── polymorphism.md └── templates.md ├── Chapter5 ├── observer.md └── soc.md ├── LICENSE ├── README.md ├── faq.md ├── getting_started.md └── terms.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.bak 2 | -------------------------------------------------------------------------------- /Chapter1/basic_structure.md: -------------------------------------------------------------------------------- 1 | # Basic Structure of a C++ Program 2 | 3 | Brief description of the structure of a C++ program. 4 | 5 | ## Preprocessor Directives 6 | 7 | Preprocessor directives are instructions to the compiler before actual compilation. 8 | Common directive: `#include` to include header files for library functions. 9 | Example: 10 | ```cpp 11 | #include // to include the input-output stream library. 12 | ``` 13 | 14 | ### Header guards 15 | 16 | All headers (.h/.hpp) you write should contain a _header guard_. 17 | Header guards are a programming technique used to prevent the multiple inclusion of the same header file throughout your project. 18 | When you `#include` a header file in multiple places within your codebase, it can lead to problems like duplicate declarations, redefinitions, and compilation errors. 19 | Header guards are a way to mitigate these issues and look like this: 20 | 21 | ##### header.hpp 22 | ```cpp 23 | #ifndef HEADER_FILENAME_H 24 | #define HEADER_FILENAME_H 25 | 26 | // Content of the header file goes here 27 | 28 | #endif 29 | ``` 30 | 31 | Note that the `HEADER_FILENAME_H` above needs to be unique for every file. 32 | 33 | #### Alternative: `#pragma once` 34 | An alternative to the above is to use the non-stanard `#pragma once` directive, which is supported by all major compilers. 35 | 36 | ##### header.hpp 37 | ```cpp 38 | #pragma once 39 | 40 | // Content of the header file goes here 41 | ``` 42 | 43 | ## Comments 44 | 45 | Comments are non-executable lines used for documentation and explanations. 46 | Single-line comments start with two forward slashes (`//`). 47 | 48 | ```cpp 49 | // This is a comment 50 | std::cout << "Hello World!"; 51 | ``` 52 | 53 | ```cpp 54 | /* The code below will print the words Hello World! 55 | to the screen, and it is amazing */ 56 | std::cout << "Hello World!"; 57 | ``` 58 | 59 | 60 | ## Semicolons 61 | 62 | Semicolons indicate the end of a statement in C++. 63 | Missing semicolons can lead to syntax errors. 64 | ```cpp 65 | int quantity = 10; 66 | double price = 5.40; 67 | double sum = price * quantity; 68 | std::cout << "Hello there" << std::endl; 69 | ``` 70 | 71 | They are also used to end class definitions. 72 | 73 | ```cpp 74 | class MyClass { 75 | // class definitions 76 | }; 77 | ``` 78 | 79 | 80 | ## Code Blocks 81 | 82 | Sections of code enclosed in curly braces `{}` are called code blocks. 83 | Blocks help organize and group statements together. 84 | 85 | Code blocks in C++ also define the scope of variables. 86 | 87 | ### Scope and Code Blocks 88 | 89 | A scope is a region of code where a variable or other named entity can be accessed and manipulated. 90 | In C++, code blocks, which are enclosed within curly braces `{}`, play a significant role in defining the 91 | scope of variables and other declarations. Here's how it works: 92 | 93 | 1. __Local Scope:__ Variables declared inside a code block are said to have local scope.They can only be accessed and used within that specific code block.Once you move outside the code block, those variables are no longer accessible. 94 | 3. __Block Nesting:__ Code blocks can be nested within each other. Variables declared in an outer block are visible to inner blocks, but not vice versa. Inner blocks can "see" variables from outer blocks, but not the other way around. 95 | 4. __Shadowing:__ If an inner block declares a variable with the same name as one in an outer block, the inner variable "shadows" the outer one within the inner block's scope. This means that the inner variable takes precedence over the outer one for the duration of that inner block. 96 | 97 | ```cpp 98 | #include 99 | 100 | int main() { 101 | int x = 5; // x is declared in the main function's scope 102 | 103 | { 104 | int x = 10; // A new x is declared in this inner code block's scope, which shadows the outer variable x. 105 | std::cout << "Inner x: " << x << std::endl; // Outputs 10 106 | } 107 | 108 | std::cout << "Outer x: " << x << std::endl; // Outputs 5 109 | 110 | return 0; 111 | } 112 | ``` 113 | 114 | Understanding scope is crucial for avoiding naming conflicts and making sure that your variables are used where and when they are intended. 115 | It's also a fundamental concept in programming languages, as it allows you to manage the visibility and accessibility of variables throughout your code. 116 | 117 | ## Statements 118 | 119 | A statement in C++ is a complete instruction that performs a specific action. 120 | Here are some common types of statements: 121 | 122 | 1. __Expression Statements:__ 123 | An expression followed by a semicolon is an expression statement.
124 | Example: `x = y + z;` or `result = 2 * (x + y);` 125 | 126 | 127 | 2. __Declaration Statements:__ 128 | These declare variables or other entities.
129 | Example: `int num;` or `double price = 10.99;` 130 | 131 | 4. __Control Flow Statements:__ 132 | These control the flow of execution in your program.
133 | Examples: `if`, `else`, `switch`, `while`, `for`, `do-while`.
134 | 135 | 5. __Jump Statements:__ 136 | These transfer control to a different part of the program.
137 | Examples: `break`, `continue`, `return`, `goto` (though rarely used). 138 | 139 | See also [Control Statements](control_statements.md) 140 | 141 | 142 | ## main() function 143 | 144 | Every C++ program must have a `main()` function. It serves as the entry point for execution. 145 | Program execution starts from the first statement in the `main()` function. 146 | ```cpp 147 | int main() { 148 | /* code here */ 149 | return 0; 150 | } 151 | ``` 152 | 153 | ## Header and source files 154 | 155 | C++ projects are often split into multiple files to keep things organized and manageable. The two main types of files you'll encounter are header files and source files. 156 | You often pair a header file with a source file. The header file defines the interface of your code, while the source file provides the actual logic and details. 157 | However, in many cases the header could contain both the definition and implementation of your code. The choice is often yours wether or not you want to split the header or not. 158 | There are advantages and disadvatages with both alternatives. 159 | 160 | You include the header file in other files using the `#include` directive. 161 | 162 | ### Header Files 163 | 164 | Header files (usually with a .h or .hpp extension) are used to declare various elements, like classes, functions, and variables, without necessarily providing their implementations. 165 | These files act as blueprints that describe what something does and how it can be used. Header files contain function prototypes (declarations) and class definitions to be re-used in other files. 166 | They don't typically contain the actual code for those functions or classes. 167 | 168 | Example of a header file named `my_class.h`: 169 | 170 | ```cpp 171 | #ifndef MY_CLASS_H 172 | #define MY_CLASS_H 173 | 174 | class MyClass { 175 | public: 176 | MyClass(); // Constructor declaration 177 | void doSomething(); // Method declaration 178 | private: 179 | int someData; // Data member declaration 180 | }; 181 | 182 | #endif 183 | ``` 184 | 185 | ### Source Files 186 | 187 | Source files (usually with a .cpp extension) contain the actual implementation of the code you declared in the header files. 188 | They include the details of how functions are defined, classes are built, and variables are used. Source files provide the "meat" of your program. 189 | 190 | Example of a source file named `my_class.cpp` implementing the `MyClass` class: 191 | 192 | ```cpp 193 | #include "my_class.h" 194 | 195 | MyClass::MyClass(): someData(0) {} // initialize the value of someData to 0 using member initializer list 196 | 197 | void MyClass::doSomething() { 198 | // Implementation of the method 199 | } 200 | ``` 201 | 202 | 203 | #### Alternative approach to splitting header 204 | 205 | As an alternative to the split above, we often can provide the header also with the implementation details: 206 | 207 | ```cpp 208 | #ifndef MY_CLASS_H 209 | #define MY_CLASS_H 210 | 211 | class MyClass { 212 | public: 213 | MyClass(): someData(0){} 214 | 215 | void doSomething() { 216 | // do something 217 | } 218 | private: 219 | int someData; 220 | }; 221 | 222 | #endif 223 | ``` 224 | 225 | As you go along, you'll learn to make more informed decisions on which approach to go with. 226 | -------------------------------------------------------------------------------- /Chapter1/control_statements.md: -------------------------------------------------------------------------------- 1 | 2 | # Control statements 3 | 4 | Control statements are essential tools in programming that enable you to control the flow of execution within your code. 5 | They allow you to make decisions, repeat actions, and create logical structures in your programs. 6 | In C++, there are three main types of control statements: 7 | [conditional statements](#Conditional-statements), [loop statements](#Loop-statements), and [branching statements](#Branching-statements). 8 | 9 | ## Conditional statements 10 | 11 | Conditional statements in C++ are tools that allow your programs to make decisions and 12 | execute different blocks of code based on certain conditions. 13 | They let your program choose between alternative paths, enabling you to create dynamic and responsive applications. 14 | Here's a concise summary of the main conditional statements you'll encounter in C++: 15 | 16 | ### `if` statement 17 | 18 | The `if` statement is used to execute a block of code if a specified condition is true. It's the simplest form of decision-making in C++. 19 | 20 | ```cpp 21 | if (condition) { 22 | // Code to be executed if condition is true 23 | } 24 | ``` 25 | 26 | ### `if-else` statement 27 | 28 | The `if-else` statement adds an alternative block of code to be executed when the condition is false. 29 | 30 | ```cpp 31 | if (condition) { 32 | // Code to be executed if condition is true 33 | } else { 34 | // Code to be executed if condition is false 35 | } 36 | ``` 37 | 38 | The shorthand if-else statement in C++ is known as the ternary operator (`?:`). 39 | It provides a compact way to write a simple conditional expression in a single line. The syntax is: 40 | 41 | ``` 42 | condition ? expression_if_true : expression_if_false; 43 | ``` 44 | 45 | For example: 46 | 47 | ```cpp 48 | int num = 10; 49 | std::string result = (num > 5) ? "Greater than 5" : "Not greater than 5"; 50 | ``` 51 | 52 | In this example, if num is greater than 5, the result will be set to "Greater than 5"; otherwise, 53 | it will be set to "Not greater than 5". 54 | 55 | 56 | ### `else-if` statement 57 | 58 | The `else-if` statement allows you to check multiple conditions sequentially. 59 | It's useful when you have more than two possible outcomes. 60 | 61 | ```cpp 62 | if (condition1) { 63 | // Code to be executed if condition1 is true 64 | } else if (condition2) { 65 | // Code to be executed if condition2 is true 66 | } else { 67 | // Code to be executed if none of the conditions are true 68 | } 69 | ``` 70 | 71 | ### `switch` statement 72 | 73 | The `switch` statement is used for multiway branching. It's often used when you have a specific value to compare against. 74 | 75 | ```cpp 76 | switch (variable) { 77 | case value1: 78 | // Code to be executed if variable == value1 79 | break; 80 | case value2: 81 | // Code to be executed if variable == value2 82 | break; 83 | // ... more cases ... 84 | default: 85 | // Code to be executed if none of the cases match 86 | } 87 | ``` 88 | 89 | In C++, when working with the switch statement, it's a good practice to use braces `{}` 90 | to define the scope of each case's code block. 91 | This helps prevent unintended behavior and makes your code more robust. 92 | Let's modify the switch example to demonstrate the importance of using braces: 93 | 94 | ```cpp 95 | switch (variable) { 96 | case value1: { 97 | // Code to be executed if variable == value1 98 | break; 99 | } 100 | case value2: { 101 | // Code to be executed if variable == value2 102 | break; 103 | } 104 | // ... more cases ... 105 | default: { 106 | // Code to be executed if none of the cases match 107 | break; 108 | } 109 | } 110 | ``` 111 | 112 | This allows you to declare a variable with the same name in each of the blocks. 113 | 114 | ### Summary 115 | 116 | - Conditional statements enable your program to respond to changing conditions and make decisions. 117 | - `if`, `if else`, and `else if` statements allow you to control program flow based on whether conditions are true or false. 118 | - The switch statement provides a way to compare a variable against multiple possible values and execute code accordingly. 119 | - Be mindful of using curly braces `{}` to define the scope of code blocks associated with conditional statements. 120 | - Proper indentation and clear formatting make your code more readable and easier to understand. 121 | 122 | Understanding and using conditional statements effectively is crucial for building flexible and interactive programs. 123 | As you practice, you'll become more adept at creating logical and responsive code that can handle various scenarios. 124 | 125 | ## Loop statements 126 | 127 | Loop statements in C++ provide you with the power to execute a block of code repeatedly, making your programs more efficient and capable of automating tasks. 128 | Here's a concise summary of the main loop statements you'll encounter in C++: 129 | 130 | ### `while` loop 131 | 132 | The `while` loop repeats a block of code as long as a specified condition remains true. 133 | It's ideal for situations where you want to repeat an action while a condition is met. 134 | 135 | ```cpp 136 | while (condition) { 137 | // Code to be repeated 138 | } 139 | ``` 140 | ### `do-while` loop 141 | 142 | The `do-while` loop is similar to the while loop, but it ensures that the code block is executed at least once before checking the condition. 143 | It's useful when you want to ensure something happens before checking if it should continue happening. 144 | 145 | ```cpp 146 | do { 147 | // Code to be repeated 148 | } while (condition); 149 | ``` 150 | 151 | ### `for` loop 152 | 153 | The for loop provides a structured way to repeat code a specific number of times or over a range of values. 154 | It's commonly used when you know the exact number of iterations needed. 155 | 156 | ```cpp 157 | for (initialization; condition; update) { 158 | // Code to repeat 159 | } 160 | ``` 161 | 162 | - __Initialization:__ This is where you set up your loop, usually by initializing a counter variable. 163 | - __Condition:__ The loop continues as long as this condition is true. 164 | - __Update:__ After each iteration, the update statement is executed, usually incrementing or decrementing the counter. 165 | 166 | #### Example: Printing numbers from 1 to 5 using a for loop. 167 | 168 | ```cpp 169 | for (int i = 1; i <= 5; i++) { 170 | std::cout << i << " "; 171 | } 172 | ``` 173 | 174 | ### `for-each` (Range-based for) loop: 175 | 176 | The `for-each` loop, also known as the range-based for loop, is used to iterate through each element in a collection, such as an array or a container. 177 | It simplifies the process of accessing elements and is great for traversing sequences. 178 | 179 | ```cpp 180 | for (data_type element : collection) { 181 | // Code to be executed for each element 182 | } 183 | ``` 184 | 185 | #### Example: Printing the values of a `std::vector` container: 186 | 187 | ```cpp 188 | std::vector v{1, 2, 3}; 189 | for (int value : v) { 190 | std::cout << value << " "; 191 | } 192 | ``` 193 | 194 | ### Summary 195 | 196 | - Loop statements help you automate repetitive tasks, iterate through data, and execute code multiple times. 197 | - Use the while loop for situations where you need to repeat code based on a condition. 198 | - The do-while loop guarantees the code block runs at least once before checking the condition. 199 | - The for loop offers a structured way to repeat code with clear initialization, condition, and update steps. 200 | - The "for each" loop simplifies iterating through collections by directly accessing each element. 201 | - Be cautious of infinite loops – make sure the loop condition can eventually become false. 202 | 203 | ## Branching statements 204 | 205 | Branching statements in C++ provide you with the ability to control the flow of your program by making decisions and altering the sequence of code execution. 206 | These statements enable your program to respond dynamically to different situations. Here's a concise summary of the main branching statements you'll encounter in C++: 207 | 208 | ### `break` statement 209 | 210 | The `break` statement is used to exit from a loop or switch statement prematurely. It allows you to immediately terminate the current loop iteration or switch case and continue executing the code after the loop or switch. 211 | 212 | ```cpp 213 | while (condition) { 214 | // Code 215 | if (some_condition) { 216 | break; // Exit the loop 217 | } 218 | // More code 219 | } 220 | ``` 221 | 222 | ### `continue` statement 223 | 224 | The `continue` statement is used to skip the rest of the current iteration of a loop and proceed to the next iteration. 225 | It's often used to avoid executing certain code within an iteration. 226 | 227 | ```cpp 228 | for (int i = 0; i < 5; ++i) { 229 | if (i == 2) { 230 | continue; // Skip iteration when i is 2 231 | } 232 | // Code to execute for each iteration 233 | } 234 | ``` 235 | 236 | ### `return` statement 237 | 238 | The `return` statement is used to exit a function and optionally return a value to the calling code. It immediately stops the function's execution and returns control to the calling context. 239 | 240 | ```cpp 241 | int square(int x) { 242 | return x * x; // Return the square of x 243 | } 244 | ``` 245 | 246 | ### Summary 247 | 248 | - Branching statements enable your program to change its behavior based on conditions or to exit from a certain block of code. 249 | - The break statement is used to exit loops or switch statements prematurely. 250 | - The continue statement skips the remaining code in the current loop iteration and moves to the next iteration. 251 | - The return statement exits a function and can return a value to the calling code. 252 | -------------------------------------------------------------------------------- /Chapter1/functions.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | Functions are fundamental building blocks of programming in C++, enabling you to organize your code, modularize tasks, and create reusable components. 4 | 5 | ## What is a Function? 6 | 7 | A function is a self-contained block of code that performs a specific task. It can take inputs, process data, and produce outputs. 8 | Compared to putting all declarations and statements inside the same code block, functions provide some benefits: 9 | 10 | - __Modularity:__ Functions allow you to break down a large problem into smaller, manageable tasks. 11 | - __Reusability:__ Once defined, functions can be reused multiple times across your codebase. 12 | - __Readability:__ Functions make your code more understandable by encapsulating complex logic. 13 | - __Maintenance:__ Changes or updates are easier to manage within a function than scattered throughout the code. 14 | 15 | 16 | ### Function Components: 17 | 18 | - __Function Signature:__ This includes the function's name, return type, and parameter list. 19 | - __Parameters:__ Parameters are inputs that you pass to the function. Functions can take zero or more parameters. 20 | - __Return Type:__ The return type specifies the type of value the function will return after performing its task. 21 | - __Function Body:__ The body contains the actual code that defines what the function does. 22 | 23 | 24 | ### Function Declaration and Definition: 25 | 26 | - __Declaration:__ Tells the compiler about the function's name, return type, and parameter list. It's usually placed in the header of your code. 27 | - __Definition:__ Provides the implementation of the function, containing the actual code that gets executed when the function is called. 28 | 29 | #### Example 30 | 31 | ```cpp 32 | // Function declaration and implementation 33 | int add(int a, int b) { 34 | return a + b; 35 | } 36 | 37 | int main() { 38 | // Function call 39 | int result = add(5, 3); 40 | return 0; 41 | } 42 | ``` 43 | 44 | In C++ the declaration and implementation may be split, either within the same file or in two separate files.The latter is known as separate compilation, which lets us split our 45 | programs into several files, each of which can be compiled independently. This allows for faster complation and hiding of implementation details. 46 | 47 | ```cpp 48 | // Function declaration 49 | int add(int a, int b); 50 | 51 | int main() { 52 | // Function call 53 | int result = add(5, 3); 54 | return 0; 55 | } 56 | 57 | // Function definition 58 | int add(int a, int b) { 59 | return a + b; 60 | } 61 | ``` 62 | 63 | To use a function, you call it by its name and provide any necessary arguments (inputs) within parentheses (call operator) as seen above. 64 | 65 | ### Function Overloading: 66 | 67 | Function overloading allows you to define multiple functions with the same name but different parameter lists. 68 | The appropriate function is selected based on the arguments provided when calling the function. 69 | 70 | This enables: 71 | 72 | - __Multiple Definitions:__ Functions with the same name but different parameter types or counts can coexist. 73 | - __Polymorphism:__ Function overloading is a form of polymorphism, where a single function name can have different implementations. 74 | - __Clearer Code:__ Overloading enhances code readability by using the same function name for related operations. 75 | 76 | #### Example 77 | 78 | ```cpp 79 | #include 80 | 81 | // Function overloading 82 | int add(int a, int b) { 83 | return a + b; 84 | } 85 | 86 | double add(double a, double b) { 87 | return a + b; 88 | } 89 | 90 | int main() { 91 | int result1 = add(5, 3); // Calls int add(int a, int b) 92 | double result2 = add(2.5, 3.7); // Calls double add(double a, double b) 93 | return 0; 94 | } 95 | ``` 96 | 97 | ## Tips for Success: 98 | 99 | - Use meaningful function names that describe what the function does. 100 | - Keep functions concise and focused on a single task. 101 | - Choose appropriate parameter names for clarity. 102 | - Use comments when needed to document the purpose and usage of each function. 103 | - Understand the concept of function overloading and how it enhances code organization and flexibility. 104 | 105 | ## The special `main` function 106 | 107 | The main function is a crucial entry point in C++ programs, serving as the starting point for program execution. 108 | It's where the program begins its journey and interacts with the user and the operating system. 109 | 110 | ### What is the Main Function? 111 | 112 | The main function is a special function that acts as the entry point of a C++ program. 113 | It's mandatory in every C++ program and serves as the starting point for executing your code. The program execution begins from the first line of the main function. 114 | 115 | The main function has a specific signature: `int main()`. It can also accept command-line arguments, such as `int main(int argc, char** argv)`. 116 | 117 | #### Example 118 | 119 | ```cpp 120 | #include 121 | 122 | int main() { 123 | // Main logic starts here 124 | std::cout << "Hello, world!" << std::endl; // Output to the console 125 | return 0; // Indicate successful program execution. If the program fails, return some positive number. You decide what the numbers mean for your application. 126 | } 127 | ``` 128 | 129 | ## Summary: 130 | 131 | Understanding functions is pivotal for creating organized, efficient, and maintainable C++ programs. 132 | 133 | Writing a good function involves following best practices to ensure your code is clear, efficient, and maintainable. Here's a concise guide on how to write a good function: 134 | 135 | - __Single Responsibility Principle (SRP):__ A function should have a clear, single purpose. If it does too many things, consider breaking it into smaller functions. 136 | - __Descriptive Names:__ Choose meaningful names for your functions that convey their purpose. A well-named function is self-documenting. 137 | - __Function Signature:__ Design the function's parameters carefully. They should provide all necessary inputs without unnecessary redundancy. 138 | - __Comments:__ Include comments when the function's purpose or behavior isn't immediately obvious from its name and code. Explain why, not just what. 139 | - __Modularity:__ Keep functions relatively small and focused. This aids readability and makes it easier to test and debug. 140 | - __Limit Side Effects:__ Minimize changes to variables outside the function's scope. This promotes predictable behavior. 141 | - __Error Handling:__ Handle errors gracefully. Either return error codes, exceptions, or use error-checking mechanisms as appropriate. 142 | - __Consistent Style:__ Follow a consistent coding style, including indentation, naming conventions, and spacing. 143 | - __Avoid Global State:__ Minimize reliance on global variables. Functions should be self-contained and not depend on external states. 144 | - __Testability:__ Write functions that are easy to test. Avoid heavy dependencies that make testing difficult. 145 | - __Performance:__ Balance readability and performance. Premature optimization can harm code clarity. 146 | - __DRY Principle (Don't Repeat Yourself):__ If a piece of code is repeated across multiple functions, consider refactoring it into a helper function. 147 | 148 | Remember that writing good functions not only makes your code better but also contributes to the overall quality and maintainability of your software projects. 149 | As you continue learning, you'll discover how functions can be combined, called, and utilized to solve a wide variety of programming challenges. 150 | -------------------------------------------------------------------------------- /Chapter1/introduction.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | 3 | Programming is the process of giving instructions to a computer to perform specific tasks. Think of it as creating a set of step-by-step directions for the computer to follow in order to achieve a desired outcome. These instructions are written using a [programming language](https://en.wikipedia.org/wiki/Programming_language), which is a special kind of language that computers can understand. 4 | 5 | Here's a simple breakdown of how programming works: 6 | 7 | 1. __Writing Code:__ Just like you write sentences to communicate with people, you write code to communicate with computers. Code consists of lines of instructions written in a programming language like C++, Python, Java, or others. 8 | 2. __Editing and Debugging:__ Sometimes, you might make mistakes in your instructions. This is similar to typos or errors in regular writing. You need to carefully review your code, find errors (bugs), and fix them. 9 | 3. __Executing Code:__ Once your code is error-free, you give it to the computer. The computer reads your instructions line by line and performs the tasks you've specified. 10 | 4. __Output:__ Depending on your instructions, the computer might perform calculations, display text, create graphics, or do various other things. This is the result of your programming effort. 11 | 5. __Iterations and Loops:__ Just like when you repeat steps to make several sandwiches, you can use loops in programming to repeat certain tasks multiple times. 12 | 6. __Conditional Statements:__ If you want the computer to make decisions based on certain conditions (e.g., "If the bread is toasted, then spread peanut butter"), you use conditional statements, similar to making choices in everyday life. 13 | 7. __Variables:__ You can also give names to values or data and store them in variables. For instance, you might have a `peanutButter` variable that holds the quantity of peanut butter you want to use. 14 | 8. __Functions:__ These are like mini-recipes that you can use over and over again. Instead of writing the same set of instructions multiple times, you can create a function and use it whenever you need. 15 | 9. __Debugging:__ Debugging is like troubleshooting. If something goes wrong, you need to identify the issue, fix it, and ensure that your code runs smoothly. 16 | 10. __Practice and Learning:__ Learning programming is like learning any other skill. You improve by practicing, experimenting, and learning from your mistakes. As you gain experience, you'll be able to create more complex and powerful programs. It helps to to have a (selfmade) problem to solve. 17 | 18 | Remember, programming is a tool that allows you to solve problems, automate tasks, and create amazing things on a computer. It might seem a bit challenging at first, but with practice and persistence, you'll become more comfortable and confident in your programming abilities. 19 | 20 | --- 21 | ### Introduction to Object-Oriented Programming (OOP) 22 | 23 | Imagine being able to model real-world objects, their behaviors, and interactions in your programs, making your code more organized, reusable, and easier to manage. OOP is a powerful approach used in programming to create well-structured and efficient software. 24 | 25 | #### What is Object-Oriented Programming? 26 | 27 | Object-Oriented Programming (OOP) is a programming paradigm that focuses on designing and organizing code around the concept of "objects." An object is a self-contained unit that combines both data (attributes) and the operations (methods or functions) that can be performed on that data. This approach helps to mimic real-world scenarios and relationships within your code. 28 | 29 | #### Key Concepts of OOP: 30 | 31 | 1. __Objects:__ Objects are the building blocks of OOP. They represent entities or things in the real world and encapsulate both data and the actions that can be performed on that data. 32 | 2. __Classes:__ A class is like a blueprint for creating objects. It defines the structure and behavior that objects of that class will have. 33 | 3. __Attributes (Properties):__ These are the data or characteristics that describe an object. For instance, if you're modeling a "Car" object, attributes might include "color," "make," and "model." 34 | 4. __Methods (Functions):__ Methods are the actions or behaviors that objects can perform. For the "Car" object, methods could be "startEngine," "accelerate," and "brake." 35 | 5. __Encapsulation:__ Encapsulation refers to the practice of bundling data (attributes) and methods (functions) that operate on that data into a single unit (object). It helps keep related code together and ensures data integrity. 36 | 6. __Inheritance:__ Inheritance allows you to create a new class (subclass) based on an existing class (superclass). The subclass inherits attributes and methods from the superclass, enabling code reuse and hierarchy. 37 | 7. __Polymorphism:__ Polymorphism allows objects of different classes to be treated as objects of a common superclass. It facilitates code flexibility and reusability through method overriding. 38 | 39 | To start with OOP, you'll learn to design classes, create objects, define attributes, implement methods, and understand the relationships between classes. Practice is key, so as you dive into OOP, don't hesitate to create small projects and explore how these concepts work in practice. 40 | 41 | Remember, OOP is a foundational concept used in various programming languages like C++, Java, Python, and more. As you become more familiar with its principles, you'll be able to design elegant and efficient solutions to complex problems in your programming journey. 42 | 43 | #### Why Learn OOP? 44 | 45 | Learning OOP has several benefits: 46 | 47 | - __Modularity:__ OOP encourages breaking down complex problems into smaller, manageable units (objects), making code more modular and maintainable. 48 | - __Code Reusability:__ With classes and inheritance, you can reuse existing code, reducing redundancy and development time. 49 | - __Abstraction:__ OOP enables you to abstract complex real-world concepts into simplified, manageable models within your code. 50 | - __Collaboration:__ OOP promotes collaboration among developers by allowing them to work on different parts of a project independently. 51 | 52 | #### OOP compared to other paradigmes 53 | 54 | | | OOP | Procedural | Functional | 55 | |--------------|------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------| 56 | | __Focus__ | OOP focuses on modeling real-world entities as objects, combining data and methods that operate on the data. | Procedural programming focuses on procedures or functions that execute a sequence of statements. | Functional programming focuses on treating computation as the evaluation of mathematical functions and avoiding mutable data. | 57 | | __Key Concepts__ | Classes, objects, inheritance, encapsulation, polymorphism. | Functions, sequences, variables. | First-class functions, immutability, higher-order functions. | 58 | | __Advantages__ | Encourages code reusability, modularity, and easy maintenance. Supports complex systems by representing entities as objects. | Simple and straightforward for smaller tasks. Emphasizes clear step-by-step procedures. | Emphasizes clear data flows and avoids side effects. Well-suited for tasks involving data transformations. | 59 | | __Examples__ | C++, Java, Python. | C, Pascal. | Haskell, Lisp, Clojure. | 60 | | __Metaphor__ | Think of objects as self-contained entities with attributes and behaviors, much like objects in the real world. | Think of programming as a series of well-defined steps or instructions, much like following a recipe. | Think of programming as composing and applying functions to data, like mathematical transformations. | 61 | 62 | ##### Commonalities and Considerations: 63 | 64 | All paradigms have their strengths and are suitable for different scenarios. 65 | OOP is useful for modeling real-world systems and creating reusable components. 66 | Procedural programming is suitable for simple tasks and clear, step-by-step procedures. 67 | Functional programming is good for tasks involving data transformations and avoiding side effects. 68 | As you explore programming paradigms, remember that they're not exclusive; you can often use elements from different paradigms to suit the needs of your projects. 69 | Understanding the strengths and characteristics of each paradigm will help you choose the right approach for your coding challenges. 70 | 71 | --- 72 | ### Introduction to C++ 73 | 74 | Imagine having the power to create software, games, and applications that can do almost anything you can think of. C++ is a programming language that allows you to turn your ideas into functional and interactive programs. 75 | 76 | #### What is C++ 77 | 78 | C++ is a versatile and widely used programming language known for its combination of power and efficiency. It was developed as an extension of the [C programming language](https://en.wikipedia.org/wiki/C_(programming_language)), adding features that make it easier to organize and manage your code. C++ is used in a wide range of applications, from creating games and mobile apps to building complex software systems and even controlling hardware devices. C++ supports OOP, as well as other programming paradigms, making in a multi-paradigm language. The focus in this course, however, lies with OOP. 79 | 80 | C++ is also a statically typed programming language, which means that the data types of variables are determined and checked at compile-time, before the program is executed. Understanding what this means is essential for writing correct and predictable code. In C++, every variable you declare must have a specific data type associated with it. This data type indicates what kind of value the variable can hold. C++ enforces this type-checking at compile-time, which is when your code is transformed into machine-executable instructions. 81 | When you declare a variable in C++, you specify its data type explicitly. For example: 82 | 83 | ```cpp 84 | int age; // 'age' is an integer variable 85 | double price; // 'price' is a double (decimal) variable 86 | ``` 87 | 88 | By specifying the data type, you're telling the compiler what kind of value the variable will hold. 89 | Static typing promotes _type safety_. It helps catch errors before your program runs. If you try to use a variable in a way that's inconsistent with its data type, the compiler will generate an error during compilation, preventing you from creating programs that could cause unexpected behavior or crashes. Overall, some of the benefits of Static Typing are: 90 | 91 | - __Predictable Behavior:__ Static typing helps you catch type-related errors early, reducing runtime surprises. 92 | - __Performance:__ Statically typed languages can often optimize code better, as the compiler knows the exact data types being used. 93 | - __Code Readability:__ By knowing the data types, other programmers can quickly understand the purpose of variables. 94 | 95 | While static typing offers numerous benefits, it can be seen as a bit more rigid compared to dynamically typed languages (e.g., Python) where variables can change types at runtime. However, this rigidity aids in preventing many types of errors. 96 | 97 | #### Why learn C++ 98 | 99 | Learning C++ opens up a world of possibilities. Here are a few reasons why you might want to learn this language: 100 | 101 | - __Versatility:__ C++ can be used to develop a wide variety of applications, from simple console programs to complex graphical user interfaces and even real-time simulations. 102 | - __Performance:__ C++ allows you to write code that runs efficiently and quickly. This is especially important for tasks that require high performance, such as games and scientific simulations. 103 | - __Game Development:__ Many popular games and game engines, like Unreal Engine, are built using C++ because of its performance and control over hardware resources. 104 | - __System Programming:__ C++ is used for creating system-level software, drivers, and operating systems. 105 | - __Career Opportunities:__ Knowledge of C++ is a valuable skill in the software development industry, opening doors to various job opportunities. 106 | 107 | You might have heard that C++ is a difficult language to learn. This is somewhat true, however, the language has evolved a lot since its inception in 1985. Todays C++ programmers should be using best practices for the language standards C++11 and newer. Also known as _modern C++_. The newer language standards have made the language much easier to use. This course uses C++17. 108 | 109 | The gist of _modern C++_ is as follows: 110 | 111 | > Modern C++ is characterized by a set of programming practices, language features, and design principles that aim to enhance code clarity, efficiency, and safety. It encourages the use of standardized libraries, smart pointers, lambda expressions, type inference, and other features to write concise, expressive, and maintainable code. The emphasis is on leveraging the C++11 and later standards to achieve a balance between high-level abstractions and low-level control while promoting best practices for memory management and code organization. 112 | 113 | #### Key concepts to be explored: 114 | 115 | As you embark on your journey with C++, here are some key concepts you'll learn about: 116 | 117 | 1. __Syntax and Basics:__ You'll learn how to write and structure C++ code, including variables, data types, input/output, and basic operations. 118 | 2. __Control Flow:__ Discover how to make decisions using conditional statements (if, else) and how to create loops to repeat tasks. 119 | 3. __Functions:__ Explore the concept of functions, which allow you to break your code into reusable blocks and simplify complex tasks. 120 | 4. __Classes and Objects:__ Dive into the world of Object-Oriented Programming (OOP). Learn about classes, objects, and how to create your own custom data types. 121 | 5. __Memory Management:__ Understand how memory is allocated and managed in C++, as well as concepts like pointers and references. 122 | 6. __C++ Standard Library:__ Explore a collection of pre-built classes and functions that provide powerful tools for common programming tasks. 123 | 124 | Learning C++ is an exciting adventure that requires practice and patience. Start by writing simple programs and gradually work your way up to more complex projects. Don't be afraid to make mistakes – they're an essential part of the learning process. There are numerous online resources, tutorials, and communities where you can ask questions and seek help. 125 | 126 | - [Stack Overflow](https://stackoverflow.com/) - This is where you often end up searching Google for help. An invaluable resource for any programmer. 127 | - [learncpp.com](https://www.learncpp.com/) - LearnCpp.com is a free website devoted to teaching you how to program in C++. 128 | - [ChatGPT](https://chat.openai.com/) - Used correctly, ChatGPT can be great companion and sparring partner. 129 | 130 | Remember, with determination and practice, you'll be well on your way to mastering C++ and creating your own remarkable software. 131 | 132 | Happy coding, and welcome to the world of C++ programming! 133 | -------------------------------------------------------------------------------- /Chapter1/operators_expressions.md: -------------------------------------------------------------------------------- 1 | # Operators and Expressions in C++ 2 | 3 | Operators are symbols that perform operations on one or more operands. 4 | Operands can be variables, constants, or other expressions. 5 | Expressions are combinations of operators and operands that produce a value. 6 | 7 | ## Types of Operators: 8 | 9 | 1. __Arithmetic Operators:__ 10 | Perform basic mathematical operations.
11 | Examples: `+` (addition), `-` (subtraction), `*` (multiplication), `/` (division), `%` (modulus). 12 | 13 | 2. __Assignment Operators:__ 14 | Assign a value to a variable.
15 | Examples: `=` (assignment), `+=` (add and assign), `-=` (subtract and assign), `*=` (multiply and assign), `/=` (divide and assign). 16 | 17 | 3. __Comparison Operators:__ 18 | Compare two values and return a Boolean result (true or false).
19 | Examples: `==` (equal to), `!=` (not equal to), `<` (less than), > (greater than), `<=` (less than or equal to), `>=` (greater than or equal to). 20 | 21 | 4. __Logical Operators:__ 22 | Perform logical operations on Boolean values.
23 | Examples: `&&` (logical AND), `||` (logical OR), `!` (logical NOT). 24 | 25 | 5. __Increment and Decrement Operators:__ 26 | Increase or decrease the value of a variable by 1.
27 | Examples: `++` (increment), `--` (decrement). 28 | 29 | 6. __Conditional (Ternary) Operator:__ 30 | A shorthand way of writing if-else statements.
31 | Syntax: `condition ? expression1 : expression2`
32 | Example: `result = (x > y) ? x : y;` 33 | 34 | 7. __Bitwise Operators:__ 35 | Perform operations on individual bits of values.
36 | Examples: `&` (bitwise AND), `|` (bitwise OR), `^` (bitwise XOR), `~` (bitwise NOT), `<<` (left shift), `>>` (right shift).` 37 | 38 | ## Expressions 39 | 40 | Expressions are combinations of operators and operands that produce a value. 41 | They can be as simple as a single variable or complex combinations of operators and functions. 42 | Expressions are used in assignments, comparisons, calculations, and more. 43 | 44 | #### Example: Arithmetic Expression 45 | 46 | ```cpp 47 | int x = 10; 48 | int y = 5; 49 | int sum = x + y; // An arithmetic expression 50 | ``` 51 | 52 | #### Example: Comparison Expression 53 | 54 | ```cpp 55 | bool result = x > y; // A comparison expression 56 | ``` 57 | 58 | #### Example: Conditional Expression 59 | 60 | ```cpp 61 | int max = (x > y) ? x : y; // A conditional expression 62 | ``` 63 | 64 | ## Using Operators and Expressions: 65 | 66 | Operators and expressions are fundamental to programming. They allow you to manipulate data, make decisions, and perform calculations. 67 | By combining operators and operands creatively, you can create complex logic and functionality in your programs. 68 | Just remember to follow operator precedence and use parentheses when necessary to ensure the desired order of operations. 69 | -------------------------------------------------------------------------------- /Chapter1/variables.md: -------------------------------------------------------------------------------- 1 | # Variables and Basic Types 2 | 3 | In C++, variables are essential components that allow you to store and manipulate data. 4 | They act like containers for holding different types of information, such as numbers, text, or even complex structures. 5 | Understanding variables is fundamental for writing effective and dynamic programs. 6 | C++ is a strongly, or statically, typed language meaning all variables have a specific type. 7 | Once a variable has been declared, the type cannot be changed unlike e.g., Python. 8 | 9 | ## Basic Data Types: 10 | 11 | Variables have data types that define the kind of value they can hold. C++ inherits a number of basic data types (known as primitives) from C, these include but are not limited to: 12 | 13 | |Type|Comment| 14 | |----|-------| 15 | | char | Single characters | 16 | | bool | Boolean values (true or false) | 17 | | int | Integers (whole numbers) | 18 | | float | Floating-point numbers (decimal numbers) | 19 | | double | Floating-point numbers with higher accuracy | 20 | 21 | For a more elaborate overview, see cppreferences' entry on [Fundamental types](https://en.cppreference.com/w/cpp/language/types) 22 | 23 | 24 | > Using the `class` or `struct` keyword in C++ you can define your own custom types. 25 | 26 | 27 | ## What is a Variable? 28 | 29 | Imagine a box where you can put something and give it a name. In programming, a variable is like that box. 30 | It's a named storage location in your computer's memory that holds a value. 31 | This value can be a number, a piece of text, or something more complex. 32 | 33 | ### Declaring a Variable: 34 | 35 | To use a variable, you need to declare it first. This means giving it a name and specifying its type (what kind of data it will hold). For example: 36 | 37 | ```cpp 38 | int age; // Declare an integer variable named 'age' 39 | double price; // Declare a double (floating-point) variable named 'price' 40 | ``` 41 | 42 | ### Assigning a Value: 43 | 44 | After declaring a variable, you can assign a value to it using the assignment operator `=`. 45 | The value you assign must match the variable's type: 46 | 47 | ```cpp 48 | age = 25; // Assign the value 25 to the 'age' variable 49 | price = 19.99; // Assign the value 19.99 to the 'price' variable 50 | ``` 51 | 52 | ### Initializing a Variable: 53 | 54 | You can also declare and assign a value in a single step, known as initialization: 55 | 56 | ```cpp 57 | int quantity = 10; // Declare and initialize the 'quantity' variable with the value 10 using direct initialization with assignment. 58 | double sum{5.0}; // Declare and initialize the 'sum' variable with the value 5.0 using uniform initialization. 59 | ``` 60 | 61 | Initializing variables when you declare them is a good programming practice with several benefits. 62 | Let's explore why you should make it a habit to initialize variables right from the start: 63 | 64 | 1. __Prevents Unintended Values:__ 65 | When you declare a variable without initializing it, it contains whatever was previously stored in that memory location. 66 | This could be garbage values, remnants of previous computations, or unpredictable data. 67 | Initializing variables ensures that they start with a known, meaningful value. 68 | 3. __Avoids Bugs and Errors:__ 69 | Using variables without initializing them can lead to bugs that are difficult to identify and fix. 70 | Unexpected behavior can occur when uninitialized variables interact with other parts of your code. 71 | Initializing variables from the beginning reduces the chances of introducing subtle errors. 72 | 3. __Enhances Readability and Intent:__ 73 | When you initialize variables at the point of declaration, you make your code more self-explanatory. 74 | Anyone reading your code can immediately understand the initial value and the intended purpose of the variable. 75 | This improves code readability and aids collaboration. 76 | 4. __Promotes Good Habits:__ 77 | Initializing variables encourages good coding habits. It forces you to think about the initial state and value that your variable should have. 78 | This practice can extend to more complex scenarios where initializing variables becomes essential for proper program behavior. 79 | 5. __Makes Debugging Easier:__ 80 | If you encounter unexpected behavior in your program, initialized variables help narrow down the scope of the issue. 81 | You can rule out uninitialized variables as a potential cause of bugs, saving time during debugging. 82 | 6. __Prevents Undefined Behavior:__ 83 | In C++, using uninitialized variables can lead to undefined behavior. 84 | The C++ standard doesn't define the behavior of your program when you read from an uninitialized variable. 85 | This means your program might work differently on different compilers or platforms. 86 | 87 | #### Example 88 | 89 | ```cpp 90 | int main() { 91 | int age; // Not initialized 92 | int salary = 0; // Initialized 93 | 94 | // Using uninitialized variable 'age' 95 | int doubleAge = age * 2; // Undefined behavior 96 | 97 | // Using initialized variable 'salary' 98 | int doubleSalary = salary * 2; // Safe and predictable 99 | 100 | return 0; 101 | } 102 | ``` 103 | 104 | ### Using Variables: 105 | 106 | Once you've declared and assigned a value to a variable, you can use it in your code. For example: 107 | 108 | ```cpp 109 | int quantity = 3; 110 | //... 111 | int total = quantity * 2; // Use the 'quantity' variable in an expression 112 | ``` 113 | 114 | ### Variable Names: 115 | 116 | Variable names can consist of letters, digits, and underscores. They must start with a letter or an underscore. 117 | Names are case-sensitive (e.g., `age` and `Age` are different variables). 118 | 119 | ### Scope: 120 | 121 | Scope defines the area of your code where a variable is visible and can be accessed. 122 | Each pair of curly braces `{}` marks a new scope. Variables declared within a scope 123 | are usually only accessible within that scope. 124 | 125 | #### Local Scope: 126 | 127 | Variables declared inside a function are local to that function's scope. 128 | They can't be accessed from outside the function. This is great for keeping 129 | variables separate and preventing unintended interference. 130 | 131 | ```cpp 132 | void printMessage() { 133 | std::string message = "Hello!"; // Local variable within the function 134 | std::cout << message << std::endl; 135 | } // 'message' goes out of scope here and is no longer accessible 136 | ``` 137 | 138 | #### Global Scope: 139 | 140 | Variables declared outside any function, at the top of your code, have global scope. 141 | They can be accessed from anywhere in your program. However, it's recommended to limit global variables 142 | as they can make code harder to understand and maintain. 143 | 144 | ```cpp 145 | #include 146 | 147 | int globalVariable = 100; // Global variable accessible everywhere 148 | 149 | void demoScopes() { 150 | int localVariable = 50; // Local variable only accessible within this function 151 | 152 | std::cout << "Global variable: " << globalVariable << std::endl; 153 | std::cout << "Local variable: " << localVariable << std::endl; 154 | } 155 | 156 | int main() { 157 | demoScopes(); 158 | // Uncommenting the next line will cause an error: 159 | // std::cout << "Local variable: " << localVariable << std::endl; 160 | 161 | return 0; 162 | } 163 | ``` 164 | 165 | ## Summary 166 | 167 | In a nutshell, variables in C++ are like named storage boxes for holding different types of information. 168 | Variables store data, and scope determines where that data is accessible. 169 | By understanding how to declare, initialize, and use variables in various scopes, 170 | you'll be able to build dynamic and organized programs. 171 | Keeping variables in appropriate scopes contributes to code clarity, prevents conflicts, 172 | and sets the foundation for creating reliable software. 173 | -------------------------------------------------------------------------------- /Chapter2/cmake_intro.md: -------------------------------------------------------------------------------- 1 | # CMake introduction 2 | 3 | [CMake](https://cmake.org/) is a powerful tool in the world of C++ programming that simplifies the process of building, 4 | compiling, and managing complex projects. It helps you create cross-platform, organized, and easily maintainable codebases. 5 | 6 | [CLion](https://www.jetbrains.com/clion/) is a cross-platform C/C++ IDE that supports CMake projects, which we will use in this course. 7 | As an NTNU student, you are [eligible for a free license](https://www.jetbrains.com/community/education/#students). 8 | 9 | 10 | ## What is CMake? 11 | 12 | CMake is a widely used open-source build system and project configuration tool. 13 | It allows developers to define and manage the build process of their C++ projects in a platform-independent way. 14 | CMake generates platform-specific build files, such as Makefiles for Unix-like systems or project files for IDEs like Visual Studio. 15 | 16 | ### Key Concepts: 17 | 18 | - __CMakeLists.txt:__ A CMakeLists.txt file is at the heart of every CMake project. It contains instructions for CMake to configure and generate build files. It specifies project details, source files, dependencies, compiler options, and more. 19 | - __Cross-Platform:__ CMake enables you to write build configurations that work across different platforms, including Windows, macOS, and various Unix-like systems. 20 | - __Generator:__ A generator is a tool that CMake uses to generate platform-specific build files. It can create Makefiles, Visual Studio project files, Xcode project files, and more. 21 | - __Target:__ In CMake, a target represents an output artifact, such as an executable, library, or other build result. Targets can have properties, dependencies, and compile options. 22 | - __Out-of-Source Build:__ CMake encourages an out-of-source build approach, where build files and generated artifacts are kept separate from the source code, reducing clutter and ensuring clean organization. 23 | 24 | ### Why Use CMake? 25 | 26 | CMake offers several benefits for C++ projects: 27 | 28 | - __Simplicity:__ CMake simplifies the process of configuring, building, and managing complex projects, abstracting away platform-specific details. 29 | - __Cross-Platform:__ With CMake, you can generate build files that work seamlessly on different platforms without significant modifications. 30 | - __Modularity:__ CMake promotes modular project organization, making it easier to manage larger codebases and libraries. 31 | - __Dependencies:__ CMake simplifies the inclusion of external libraries and dependencies into your project. 32 | - __IDE Integration:__ CMake integrates with various Integrated Development Environments (IDEs), allowing you to work within your preferred coding environment. 33 | 34 | #### Going further: 35 | 36 | - __Specify include directories__: Use `target_include_directories` to specify search folders for headers. 37 | - __Create Libraries__: Use `add_library` to create modular and re-usable code. 38 | - __Adding Libraries:__ Use `target_link_libraries` to link your executable with your own or external libraries. 39 | - __Organizing Code:__ Create subdirectories for different parts of your project and use `add_subdirectory` in your `CMakeLists.txt`. 40 | 41 | ## Organizing CMake projects 42 | 43 | Organizing a CMake project effectively is crucial for maintaining a clean, structured, and manageable codebase. 44 | Here's a recommended organization structure for your CMake project: 45 | 46 | - __Root Directory:__ 47 | The root directory of your project contains the main `CMakeLists.txt` file and any top-level project files. 48 | - This directory often has a name that reflects your project's purpose. 49 | 50 | ```scss 51 | MyProject/ 52 | ├── CMakeLists.txt (Main CMake configuration) 53 | ├── README.md 54 | ├── LICENSE 55 | ├── .gitignore 56 | ├── build/ (auto-generated build directory) 57 | ├── include/ (Header files) 58 | ├── src/ (Source files) 59 | ├── test/ (Test folder) 60 | └── data/ (Additional resources for your project) 61 | ``` 62 | 63 | Your main `CMakeLists.txt` file should be in the root directory. If you have subdirectories, create additional `CMakeLists.txt` files in those directories to configure the build for their specific contents. 64 | 65 | - __Build Directory:__ 66 | Create a separate build directory (e.g., `build/`) to keep build-related files separate from the source code. This prevents cluttering your source directory with build artifacts. 67 | - __Include Directory:__ 68 | Place header files (.h or .hpp) in the `include/` directory. Organize headers based on their functionality or modules. Use proper directory structure to prevent naming clashes and enhance code readability. 69 | - __Source Directory:__ 70 | The `src/` directory is where your source code files (.cpp files) reside. Similar to headers, organize source files by functionality or modules to maintain clarity. 71 | - __Subdirectories__: 72 | Consider creating subdirectories within `include/` and `src/` as your project grows. This helps maintain a well-structured codebase. For example: 73 | 74 | ``` 75 | ... 76 | include/ 77 | ├── math/ 78 | │ ├── arithmetic.h 79 | │ └── geometry.h 80 | └── utils/ 81 | ├── string_utils.h 82 | └── file_utils.h 83 | src/ 84 | ├── CMakeLists.txt 85 | ├── math/ 86 | │ ├── arithmetic.cpp 87 | │ └── geometry.cpp 88 | └── utils/ 89 | ├── string_utils.cpp 90 | └── file_utils.cpp 91 | ``` 92 | 93 | - __Testing:__ 94 | For testing purposes, create a separate directory like `test/` or `tests/` to hold your unit tests. This keeps test code separate from your source code. 95 | 96 | - __Documentation:__ 97 | Consider having a `docs/` directory to store any documentation related to your project. 98 | 99 | - __Additional Files:__ 100 | - Include a `README.md` file to provide an overview of your project, its purpose, and how to use it. 101 | - Include a `.gitignore` file to specify which files and directories should be ignored by version control. 102 | - Include a `LICENSE` [file](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/licensing-a-repository) to state the terms under which your code is shared. 103 | 104 | By organizing your CMake project in this manner, you'll make it easier for yourself and other developers to navigate, understand, and maintain your codebase as it grows in complexity. 105 | For more information see [The Pitchfork Layout](https://api.csswg.org/bikeshed/?force=1&url=https://raw.githubusercontent.com/vector-of-bool/pitchfork/develop/data/spec.bs). 106 | 107 | 108 | ## Manually creating a CMake project 109 | 110 | In your selected project directory, create a file named `CMakeLists.txt`. This file contains instructions for CMake to configure and generate build files. 111 | 112 | > Note: Creating a new Project in CLion will do this for you. 113 | 114 | Here's a basic `CMakeLists.txt` example: 115 | 116 | ```cmake 117 | # Specify the minimum required version of CMake. 118 | cmake_minimum_required(VERSION 3.15) 119 | 120 | # Define the project name. 121 | project(MyProject) 122 | 123 | # The 'project' command above initializes the project. 124 | # Now, let's add our source files to be compiled into an executable. 125 | # 'add_executable' associates source files with the project and generates the executable. 126 | add_executable(MyExecutable main.cpp) 127 | 128 | # Note: You can add more source files by extending the 'add_executable' line. 129 | # For example, 'add_executable(MyExecutable main.cpp another_file.cpp)' 130 | ``` 131 | 132 | ### Building from the Command Line 133 | 134 | CMake projects can be built using the Command Line using a variation of the commands below. 135 | 136 | ``` 137 | //Windows 138 | cmake . -A x64 -B build -DCMAKE_BUILD_TYPE=Release 139 | cmake --build build --config Release 140 | 141 | //Linux & Mac 142 | cmake . -B build -DCMAKE_BUILD_TYPE=Release 143 | cmake --build build 144 | ``` 145 | 146 | This assumes that `cmake` command is available. That is, CMake is installed and globally available on the system i.e. added to PATH. 147 | 148 | ## Summary 149 | 150 | Mastering CMake will empower you to efficiently manage your C++ projects, create consistent and reliable build systems, and collaborate effectively in the world of software development. 151 | -------------------------------------------------------------------------------- /Chapter2/data_structures.md: -------------------------------------------------------------------------------- 1 | # Data structures 2 | 3 | A data structure is a way of organizing and storing data in memory to efficiently perform various operations on that data. 4 | Data structures provide a way to manage and manipulate data elements, making it easier to perform tasks like insertion, deletion, searching, and sorting. 5 | 6 | Here is an overview of common data structures: 7 | 8 | - __Arrays:__ An array is a collection of elements of the same data type stored in contiguous memory locations. Elements can be accessed using an index. Arrays have a fixed size that needs to be specified during declaration. 9 | 10 | - __Vectors:__ Vectors are a dynamic array implementation provided by the C++ Standard Library (STL). They can dynamically resize themselves, making them more flexible than traditional arrays. Vectors provide methods for adding, removing, and accessing elements. 11 | 12 | - __Linked Lists:__ A linked list is a data structure consisting of nodes, where each node contains data and a reference (or pointer) to the next node in the sequence. Linked lists can be singly linked (each node points to the next) or doubly linked (each node points to both the next and previous nodes). 13 | 14 | - __Stacks:__ A stack is a linear data structure that follows the Last-In-First-Out (LIFO) principle. Elements are added and removed from the top of the stack. Common operations include push (add) and pop (remove) operations. 15 | 16 | - __Queues:__ A queue is a linear data structure that follows the First-In-First-Out (FIFO) principle. Elements are added at the back (enqueue) and removed from the front (dequeue) of the queue. 17 | 18 | - __Trees:__ Trees are hierarchical data structures composed of nodes. Each tree has a root node, and every node can have child nodes. Binary trees, binary search trees, and AVL trees are common variations. 19 | 20 | - __Graphs:__ Graphs are a collection of nodes connected by edges. They can be directed (edges have a direction) or undirected. Graphs are used to represent relationships between elements. 21 | 22 | - __Sets and Maps:__ Sets store unique elements in no particular order, while maps (also known as dictionaries) store key-value pairs. C++ provides `std::set`, `std::unordered_set`, `std::map`, and `std::unordered_map` as part of the STL. 23 | 24 | These are just some of the common data structures in C++. Choosing the right data structure depends on the problem you're trying to solve and the efficiency you require for different operations. 25 | The C++ Standard Library provides implementations of many of these data structures. 26 | 27 | ## Sequence containers 28 | 29 | Sequence containers refer to a group of container class templates in the standard library of the C++ programming language that implement storage of data elements. 30 | Being templates, they can be used to store arbitrary elements, such as integers or custom classes. 31 | 32 | The following containers are defined in the current revision of the C++ standard: `array`, `vector`, `list`, `forward_list`, `deque`. 33 | Each of these containers implements different algorithms for data storage, which means that they have different speed guarantees for different operations 34 | 35 | ### Arrays 36 | 37 | As mentioned, Arrays have a fixed size that needs to be specified during declaration. C provides a built-in array type, however, 38 | the C++ Standard Library provides a better option through its `std::array` type. 39 | 40 | #### C-style array 41 | ```cpp 42 | #include 43 | 44 | int main() { 45 | // Declare and initialize C-style array of integers with a fixed size 46 | int myArray[5] = {10, 20, 30, 40, 50}; 47 | 48 | // Access and print array elements 49 | for (int i = 0; i < sizeof(myArray) / sizeof(int); ++i) { 50 | std::cout << "Element at index " << i << ": " << myArray[i] << std::endl; 51 | } 52 | 53 | return 0; 54 | } 55 | ``` 56 | 57 | #### std::array 58 | 59 | ```cpp 60 | #include 61 | #include 62 | 63 | int main() { 64 | // Declare and initialize an std::array of integers 65 | std::array myArray = {10, 20, 30, 40, 50}; 66 | 67 | // Access and print array elements 68 | for (int i = 0; i < myArray.size(); ++i) { // alternativly, use for-each 69 | std::cout << "Element at index " << i << ": " << myArray[i] << std::endl; 70 | } 71 | 72 | return 0; 73 | } 74 | ``` 75 | 76 | #### Difference between C Arrays and C++ Arrays: 77 | 78 | 1. __Declaration and Initialization:__ 79 | 80 | - In C, you typically declare and initialize arrays separately, like `int myArray[5];` followed by assignment of values. 81 | - In C++, you can combine declaration and initialization using the curly braces initializer syntax, like `int myArray[5] = {10, 20, 30, 40, 50};`. 82 | 83 | 2. __Bound Checking:__ 84 | 85 | - C arrays do not perform bounds checking. Accessing an index outside the array's bounds can lead to undefined behavior, like accessing memory that doesn't belong to the array. 86 | - C++ STL containers like `std::array` and `std::vector` (which are similar to arrays) perform bounds checking and throw exceptions when accessing out-of-bounds indices. 87 | 88 | 3. __Passing to Functions:__ 89 | 90 | - In C, when you pass an array to a function, you're actually passing a pointer to the first element. There's no inherent mechanism to know the size of the array so you'll need to pass an additional size parameter alongside the array. 91 | - In C++, you can use `std::array` to pass arrays with size information. 92 | 93 | 4. __Copying and Assignment:__ 94 | 95 | - C arrays don't have built-in copy mechanisms or assignment operators. 96 | - C++ arrays (using `std::array`) can be copied directly using the assignment operator, and the copy will contain the same elements. 97 | 98 | In summary, while C arrays and C++ arrays share some similarities, C++ introduces improvements and safer alternatives through the Standard Library, like `std::array` and containers such as `std::vector`. 99 | These improvements help in avoiding common pitfalls associated with C arrays. 100 | 101 | ### Vectors 102 | 103 | A vector is essentially a dynamic array. It automatically handles memory allocation and resizing as elements are added or removed. 104 | This makes vectors more versatile than traditional arrays, which have a fixed size. 105 | 106 | ```cpp 107 | #include 108 | #include 109 | 110 | int main() { 111 | // Create a vector of integers initialized with some elements 112 | std::vector myVector {1 ,2 3}; 113 | 114 | // Add additional elements to the vector 115 | myVector.emplace_back(10); 116 | myVector.emplace_back(20); 117 | myVector.emplace_back(30); 118 | 119 | // Iterate through the vector using a range-based loop 120 | for (int element : myVector) { 121 | std::cout << element << " "; 122 | } 123 | std::cout << std::endl; 124 | 125 | return 0; 126 | } 127 | ``` 128 | 129 | Unless you have a very good reason not to, `std::vector` should be used for storing elements in a list-like structure. 130 | 131 | ### Linked-list 132 | 133 | A linked list is a data structure to organize and store a collection of elements, where each element is represented by a node. Unlike arrays or vectors, which use contiguous memory to store elements, a linked list consists of a series of nodes, where each node contains both the actual data and a pointer to the next node in the list. This chain of nodes forms a linear sequence. 134 | 135 | ##### Implementation of a singly-linked list 136 | ```cpp 137 | #include 138 | 139 | // Define a class for the linked list 140 | template 141 | class LinkedList { 142 | 143 | private: 144 | 145 | // Define the structure for a singly linked list node 146 | template 147 | struct Node { 148 | E data; 149 | Node* next; 150 | 151 | Node(E value) : data(value), next(nullptr) {} 152 | }; 153 | 154 | size_t size_; 155 | Node* head_; 156 | 157 | public: 158 | LinkedList() : head_(nullptr) {} 159 | 160 | size_t size() const { 161 | 162 | return size_; 163 | } 164 | 165 | T &operator[](size_t index) { 166 | if (index > size_) { 167 | throw std::runtime_error("Index out of bounds: " + std::to_string(index)); 168 | } 169 | 170 | auto* current = head_; 171 | for (int i = 0; i < index; i++) { 172 | current = current->next; 173 | } 174 | return current->data; 175 | } 176 | 177 | void insert(T value) { 178 | auto newNode = new Node(value); 179 | if (!head_) { 180 | head_ = newNode; 181 | } else { 182 | auto* current = head_; 183 | while (current->next) { 184 | current = current->next; 185 | } 186 | current->next = newNode; 187 | } 188 | ++size_; 189 | } 190 | 191 | // Destructor to release memory occupied by nodes 192 | ~LinkedList() { 193 | auto* current = head_; 194 | while (current) { 195 | auto* temp = current; 196 | current = current->next; 197 | delete temp; 198 | } 199 | } 200 | }; 201 | 202 | int main() { 203 | LinkedList list; 204 | 205 | list.insert(10); 206 | list.insert(20); 207 | list.insert(30); 208 | list.insert(40); 209 | 210 | for (int i = 0; i < list.size(); i++) { 211 | std::cout << list[i] << " "; 212 | } 213 | std::cout << std::endl 214 | 215 | return 0; 216 | } 217 | ``` 218 | 219 | ## Maps 220 | 221 | Maps (also known as dictionaries) store key-value pairs. 222 | 223 | ```cpp 224 | #include 225 | #include 226 | #include 227 | 228 | int main() { 229 | // Using std::map (ordered map) 230 | std::map orderedMap; 231 | orderedMap[3] = "Apple"; 232 | orderedMap[1] = "Banana"; 233 | orderedMap[2] = "Orange"; 234 | 235 | std::cout << "Ordered Map:" << std::endl; 236 | for (const auto& entry : orderedMap) { 237 | std::cout << entry.first << ": " << entry.second << std::endl; 238 | } 239 | 240 | // Using std::unordered_map (unordered hash map) 241 | std::unordered_map unorderedMap; 242 | unorderedMap[3] = "Cat"; 243 | unorderedMap[1] = "Dog"; 244 | unorderedMap[2] = "Elephant"; 245 | 246 | std::cout << "Unordered Map:" << std::endl; 247 | for (const auto& entry : unorderedMap) { 248 | std::cout << entry.first << ": " << entry.second << std::endl; 249 | } 250 | 251 | return 0; 252 | } 253 | ``` 254 | 255 | #### Differences between std::map and std::unordered_map: 256 | 257 | 1. __Ordering:__ 258 | 259 | - `std::map`: Stores elements in a sorted order based on the keys. 260 | - `std::unordered_map`: Does not guarantee any specific order of elements. 261 | 262 | 2. __Performance:__ 263 | 264 | - `std::map`: Provides slower insertion and lookup times compared to std::unordered_map. Insertions and lookups have logarithmic time complexity. 265 | - `std::unordered_map`: Provides faster insertion and lookup times on average, typically with constant-time complexity. 266 | 267 | 3. __Underlying Data Structure:__ 268 | 269 | - `std::map`: Typically implemented as a balanced binary search tree (such as a red-black tree). 270 | - `std::unordered_map`: Implemented using a hash table. 271 | 272 | 4. __Key Type Requirements:__ 273 | 274 | - `std::map`: Requires that the key type supports comparison operations (e.g., less than) to maintain order. 275 | - `std::unordered_map`: Requires that the key type supports hash functions and equality comparisons. 276 | 277 | 5. __Memory Usage:__ 278 | 279 | - `std::map`: Generally uses more memory due to the tree structure and additional pointers. 280 | - `std::unordered_map`: Memory usage depends on the load factor and hash function quality, but it can be more memory-efficient in some cases. 281 | 282 | 6. __Use Cases:__ 283 | 284 | - `std::map`: Suitable when maintaining order is important or when the keys are naturally ordered. 285 | - `std::unordered_map`: Suitable for fast data retrieval when order doesn't matter and hash-based lookup is efficient. 286 | 287 | When choosing between `std::map` and `std::unordered_map`, consider the specific requirements of your application. 288 | If you need fast insertion and lookup times and order is not important, `std::unordered_map` might be a better choice. 289 | If you need to maintain elements in a sorted order, then `std::map` is more appropriate. 290 | 291 | ### Trees 292 | 293 | A tree is a widely used abstract data type that represents a hierarchical tree structure with a set of connected nodes. Each node in the tree can be connected to many children (depending on the type of tree), but must be connected to exactly one parent, except for the root node, which has no parent (i.e., the root node as the top-most node in the tree hierarchy). These constraints mean there are no cycles or "loops" (no node can be its own ancestor), and also that each child can be treated like the root node of its own subtree, making recursion a useful technique for tree traversal. 294 | 295 | ##### Implementation of a tree in C++ 296 | 297 | ```cpp 298 | template 299 | class node { 300 | 301 | public: 302 | node(T value) : value_(std::move(value)) {} 303 | 304 | T &value() { 305 | return value_; 306 | } 307 | 308 | const T &value() const { 309 | return value_; 310 | } 311 | 312 | [[nodiscard]] bool hasParent() const { 313 | return parent_ != nullptr; 314 | } 315 | 316 | const node *parent() const { 317 | return parent_; 318 | } 319 | 320 | const std::vector>> &children() const { 321 | return children_; 322 | } 323 | 324 | node &addChild(T child) { 325 | children_.emplace_back(std::make_unique>(child)); 326 | children_.back()->parent_ = this; 327 | return *(children_.back()); 328 | } 329 | 330 | [[nodiscard]] size_t numChildren() const { 331 | return children_.size(); 332 | } 333 | 334 | // implementing depth-first traversal using recursion 335 | void traverse(const std::function &)> &f) override { 336 | f(*this); 337 | for (auto &child: children_)) { 338 | child->traverse(f); 339 | } 340 | } 341 | 342 | private: 343 | T value_; 344 | node *parent_ = nullptr; 345 | std::vector>> children_; 346 | }; 347 | ``` 348 | 349 | -------------------------------------------------------------------------------- /Chapter2/platformio.md: -------------------------------------------------------------------------------- 1 | # PlatformIO 2 | 3 | [PlatformIO](https://platformio.org/) is a cross-platform, cross-architecture, multiple framework, 4 | professional tool for embedded systems engineers and for software developers who write applications for embedded products. 5 | 6 | In short, PlatformIO enables you to target different embedded systems, including Arduino, using the same toolchain. 7 | 8 | ## Installation 9 | 10 | Please refer to the [PlatformIO Core (CLI) installation documentation](https://docs.platformio.org/en/latest/core/installation/index.html). 11 | 12 | > Do rememeber to read __Install Shell Commands__ 13 | 14 | ### Using PlatformIO with CLion 15 | 16 | Recent versions of CLion has first class support for PlatformIO projects. CLion's integration with PlatformIO is provided by the 17 | [PlatformIO for CLion plugin](https://plugins.jetbrains.com/plugin/13922-platformio-for-clion?_ga=2.123214155.1374059959.1693506296-72150495.1693506296), 18 | created in collaboration with the PlatformIO team. 19 | 20 | This allows you you write code for both desktop C++ and Arduino within the same editor. No need to use CLion for one thing and Arduino 21 | editor for another and it allows you to use proffesional tooling also for your Arduino projects. 22 | 23 | Please have a look at CLion's [Installation section](https://www.jetbrains.com/help/clion/platformio.html#install) for PlatformIO. 24 | -------------------------------------------------------------------------------- /Chapter2/standard_library.md: -------------------------------------------------------------------------------- 1 | # The C++ Standard Library 2 | 3 | The **C++ Standard Library** is a comprehensive collection of pre-built functions, classes, and templates that accompanies the C++ programming language. 4 | This library provides a rich toolkit to simplify and expedite the process of developing C++ programs. It covers a broad spectrum of functionalities, 5 | ranging from data structures and algorithms to input/output, strings, concurrency, and more. 6 | 7 | ## Purpose and Benefits 8 | 9 | The core purpose of the C++ Standard Library is to equip developers with a set of well-tested and optimized tools. 10 | These tools can be readily used to design efficient, reliable, and maintainable C++ applications. By leveraging the library's components, 11 | developers can avoid the need to reinvent common solutions, saving both time and effort. 12 | 13 | Key advantages of using the C++ Standard Library include: 14 | 15 | - **Enhanced Efficiency:** Developers can code more rapidly by utilizing pre-implemented data structures and algorithms. 16 | - **Improved Code Quality:** The library's components are thoroughly tested, minimizing errors and boosting code reliability. 17 | - **Optimized Performance:** Many library components are designed for performance, leading to faster and more efficient code. 18 | - **Platform Portability:** The library is designed to work seamlessly across different platforms, ensuring code portability. 19 | 20 | ## Core Components and Headers 21 | 22 | The C++ Standard Library is organized into several categories, addressing diverse programming needs. 23 | Here are some the core components, along with their corresponding headers and short descriptions: 24 | 25 | ### Containers 26 | 27 | - **Vector:** `` - Dynamic array with efficient resizing and random access. 28 | - **List:** `` - Doubly linked list with efficient insertions and deletions. 29 | - **Deque:** `` - Double-ended queue with efficient insertions and deletions at both ends. 30 | - **Map:** `` - Associative container for key-value pairs, allowing fast lookup. 31 | - **Set:** `` - Collection of unique, sorted elements. 32 | - **Stack:** `` - Last-In-First-Out (LIFO) data structure. 33 | - **Queue:** `` - First-In-First-Out (FIFO) data structure. 34 | 35 | ### Streams 36 | 37 | - **iostream:** `` - Input and output streams for reading and writing to various sources. 38 | - **fstream:** `` - Input and output operations for files. 39 | - **sstream** `` - Treating strings as streams for data manipulation. 40 | 41 | ### Smart Pointers 42 | 43 | - **unique_ptr:** `` - Ownership-based smart pointer for automatic memory management. 44 | - **shared_ptr:** `` - Shared ownership smart pointer for resource management. 45 | - **weak_ptr:** `` - Non-owning observer to a shared_ptr, avoiding cyclic references. 46 | 47 | ### Concurrency 48 | 49 | - **Threads:** `` - Creation and management of threads for parallel execution. 50 | - **Mutexes:** `` - Mutual exclusion mechanisms to protect shared resources. 51 | - **Condition Variables:** `` - Synchronization of threads based on conditions. 52 | - **Futures and Promises:** `` - Management of asynchronous computations and results. 53 | 54 | 55 | ### Misc 56 | 57 | - **Algorithms:** `` - Collection of generic algorithms for common operations. 58 | - **Utility:** `` - Various utilities including pairs and `std::move`. 59 | - **Optional:** `` - A container for optional values that can be empty. 60 | - **Strings:** `` - Class for versatile string manipulation. 61 | - **Filesystem:** `` - Operations for working with files and directories. 62 | - **Time and Date:** `` - Facilities for handling time points, durations, and intervals. 63 | - **Mathematics:** `` - Mathematical functions, constants, and utilities. 64 | 65 | 66 | ## How to Use 67 | 68 | To utilize the C++ Standard Library, simply include the appropriate header in your code. 69 | For instance, if you need to use the `vector` container, include the `` header. 70 | 71 | ```cpp 72 | #include 73 | 74 | int main() { 75 | std::vector numbers = {1, 2, 3, 4}; 76 | // Utilize vector operations here 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /Chapter2/version_control.md: -------------------------------------------------------------------------------- 1 | # Introduction to Version Control and Git 2 | 3 | Version control is a crucial aspect of modern software development that helps teams collaborate efficiently and manage changes to their codebase. 4 | It allows developers to track modifications to files over time, work on different features simultaneously, and revert to previous states if necessary. 5 | One of the most widely used version control systems is [Git](https://git-scm.com/). 6 | 7 | ## What is Version Control? 8 | 9 | Version control, also known as source control or revision control, is a system that helps developers track changes to their codebase and collaborate effectively with others. 10 | It provides a structured way to manage different versions of files, enabling developers to work on projects collaboratively without overwriting each other's work. 11 | 12 | ## Key Concepts in Version Control 13 | 14 | ### Repositories 15 | 16 | A **repository** is a central storage unit where all versions of your project's files, along with their complete history, are stored. It serves as the single source of truth for your codebase. 17 | 18 | ### Commits 19 | 20 | A **commit** represents a specific snapshot of your project at a given point in time. It captures changes to files, including additions, modifications, and deletions. Each commit is accompanied by a unique identifier and a commit message that explains the changes. 21 | 22 | ### Branching 23 | 24 | **Branching** allows developers to create separate lines of development within a repository. Each branch can be worked on independently, enabling the implementation of new features or bug fixes without affecting the main codebase. Branches can later be merged back together. 25 | 26 | ### Merging 27 | 28 | **Merging** is the process of combining changes from one branch into another. It enables collaboration between developers working on different features or bug fixes. 29 | 30 | ### Pull Requests 31 | 32 | A **pull request** or PR is a mechanism for proposing changes from one branch to another. It facilitates code review and discussion before changes are merged into the main codebase. 33 | 34 | ### Forks 35 | 36 | A **fork** is a separate copy of a repository. It allows developers to experiment with changes without affecting the original project. Forked repositories can be kept up-to-date with the original through synchronization. 37 | 38 | ## Introducing Git 39 | 40 | **Git** is a distributed version control system designed to handle everything from small to very large projects with speed and efficiency. 41 | Created by Linus Torvalds in 2005, Git revolutionized the way developers collaborate and manage their source code. 42 | 43 | ### Key Features of Git 44 | 45 | - **Distributed Nature**: Unlike centralized version control systems, Git is distributed. Each developer has a complete copy of the repository, enabling them to work offline and facilitating parallel development. 46 | - **Branching and Merging**: Git excels at branching, allowing developers to create separate lines of development. Branches can be easily merged, enabling the incorporation of new features without disrupting the main codebase. 47 | - **Lightweight and Fast**: Git's architecture is designed for speed, making it quick to commit changes, switch between branches, and track history. 48 | - **Commit History**: Git maintains a detailed history of commits, showcasing who made what changes and when. This audit trail is invaluable for troubleshooting, accountability, and understanding project evolution. 49 | - **Open Source and Widely Adopted**: Being open source, Git is accessible to everyone. Its popularity and community support have led to a wealth of tools, integrations, and resources. 50 | 51 | 52 | ## Resources and tutorial 53 | 54 | - [Official Git tutorial](https://git-scm.com/docs/gittutorial) 55 | - ["Become a git guru" by Atlassian](https://www.atlassian.com/git/tutorials) 56 | - [CLion: Working with Git tutorial](https://www.jetbrains.com/help/clion/working-with-git-tutorial.html) 57 | -------------------------------------------------------------------------------- /Chapter3/classes.md: -------------------------------------------------------------------------------- 1 | # Introducing Classes in C++ 2 | 3 | Classes are a fundamental concept in C++ and form the basis of 4 | object-oriented programming (OOP). They allow you to define custom data 5 | types that represent real-world objects or concepts in your program. 6 | 7 | ## What is a Class? 8 | 9 | A class in C++ serves as a blueprint or template for creating objects. 10 | It defines the properties (data members) and behaviors (member functions or methods) 11 | that objects of that class will have. Essentially, a class helps you model 12 | real-world entities or concepts in your program. 13 | 14 | ## Class Declaration 15 | 16 | To declare a class, you use the `class` keyword followed by the class name. 17 | Here's a simple example of a class declaration for a "Car": 18 | 19 | ```cpp 20 | class Car { 21 | 22 | public: 23 | // Data members (properties) 24 | std::string brand; 25 | std::string model; 26 | int year; 27 | 28 | // Member functions (methods) 29 | void startEngine(); 30 | void stopEngine(); 31 | void accelerate(); 32 | void brake(); 33 | }; // <--- NOTE the ; 34 | ``` 35 | 36 | In this example: 37 | 38 | - `Car` is the class name 39 | - `brand`, `model`, and `year` are data members representing the car`s properties. 40 | - `startEngine`, `stopEngine`, `accelerate`, and `brake` are member functions 41 | representing the car's behaviour. 42 | - In this example, the header only provides the declaration. 43 | The actual implementation code (what happens when the functions are called) 44 | would be defined in a separate source file (.cpp). This is known as splitting the header. 45 | 46 | ## Creating objects 47 | 48 | Once you've defined a class, you can create objects (instances) of that class. 49 | Objects are actual instances of the class that can hold data and perform actions. 50 | Here's how you create a `Car` object: 51 | 52 | ```cpp 53 | Car myCar; // Creating a Car object named "myCar" 54 | ``` 55 | 56 | ## Accessing members 57 | 58 | You can access the data members (fields) and member functions of an object 59 | using the dot (`.`) operator: 60 | 61 | ```cpp 62 | Car myCar; 63 | myCar.brand = "Toyota"; 64 | myCar.model = "Camry"; 65 | myCar.year = 2020; 66 | 67 | myCar.startEngine(); 68 | myCar.accelerate(); 69 | ``` 70 | 71 | In the above, the fields and functions are accessible outside the class 72 | as they are declared under a `public` access specifier. Typically, we would employ [encapsulation](#encapsulation) 73 | and disallow access to internal data fields as well as initializing key data fields directly using a [constructor](#constructor). 74 | 75 | ## Member Functions 76 | 77 | Member functions are used to define the actions that objects of the class can perform. 78 | They are defined inside the class and may be implemented outside the class. 79 | For example: 80 | 81 | ```cpp 82 | void Car::startEngine() { 83 | cout << "Engine started." << endl; 84 | } 85 | 86 | void Car::accelerate() { 87 | cout << "Car is accelerating." << endl; 88 | } 89 | ``` 90 | 91 | > Note: [ClassName::] (i.e Car:: above) is only used when functions are implemented outside the class body. 92 | 93 | ## Constructor 94 | 95 | A constructor is a special member function that is called when an object is created. 96 | It initializes the object's data members. 97 | Here's an example of a constructor for the `Car` class: 98 | 99 | ```cpp 100 | Car::Car(std::string brand, std::string model, int year) { 101 | this->brand = brand; 102 | this->model = model; 103 | this->year = year; 104 | } 105 | ``` 106 | 107 | The above is allowed and is similar to how member attributes in other languages are set. However, you __ought__ to be using [member initializer lists](https://en.cppreference.com/w/cpp/language/constructor): 108 | 109 | ```cpp 110 | Car::Car(std::string brand, std::string model, int year) 111 | : brand(brand), 112 | model(model), 113 | year(year) {} 114 | ``` 115 | 116 | ## Using Constructors 117 | 118 | You can use constructors to initialize objects when they are created: 119 | 120 | ```cpp 121 | Car myCar("Ford", "Mustang", 2022); 122 | ``` 123 | 124 | The type and number of parameters provided as input to the constructor must match. 125 | A class can have multiple constructors. In the example above, 126 | if `Car(std::string brand, std::string model, int year)` is the only constructor defined, 127 | you are not allowed to do: 128 | 129 | ```cpp 130 | Car myCar1; // compile error - no matching constructor 131 | Car myCar2("Ford", "Mustang"); // compile error - still no matching constructor 132 | Car myCar3("Ford", "Mustang", 2022); // valid code 133 | ``` 134 | 135 | ## Encapsulation 136 | Encapsulation is a key principle of OOP, and it involves bundling data and methods that operate on that data into a single unit (the class). 137 | In C++, you can control access to the data members using access specifiers like `public`, `private`, and `protected`. 138 | 139 | - `public`: Members declared as public are accessible from outside the class. 140 | - `private`: Members declared as private are only accessible from within the class. 141 | - `protected`: Members declared as protected are accessible from within the class and derived classes (inheritance). 142 | 143 | Encapsulation is like putting your data and the code that works with that data into a box and allowing controlled access to the box. 144 | This helps in hiding the internal details of how a class works and provides a well-defined interface for interacting with it. 145 | 146 | ### Why Encapsulation? 147 | - __Data Protection:__ Encapsulation helps protect data from unauthorized access or modification. By defining data members as private or protected, you can control who or what can change the internal state of an object. 148 | - __Abstraction:__ It allows you to provide a simplified view of an object to the outside world. Users of a class don't need to know the intricate details of how it works internally; they only need to interact with its public interface. 149 | - __Code Organization:__ Encapsulation promotes clean and organized code. Data and related functions are grouped together within a class, making the codebase more maintainable. 150 | 151 | Here's an example demonstrating encapsulation in C++: 152 | 153 | ```cpp 154 | class BankAccount { 155 | 156 | private: 157 | double balance_; // Private data member 158 | 159 | public: 160 | // Constructor 161 | BankAccount(double initialBalance) 162 | : balance_(initialBalance){} 163 | 164 | // Public member functions 165 | void deposit(double amount) { 166 | if (amount > 0) { 167 | balance_ += amount; 168 | } 169 | } 170 | 171 | void withdraw(double amount) { 172 | if (amount > 0 && balance_ >= amount) { 173 | balance_ -= amount; 174 | } 175 | } 176 | 177 | double getBalance() { 178 | return balance_; 179 | } 180 | }; 181 | ``` 182 | 183 | The private data is encapsulated within the class, and external code can interact with the object only through the public functions, 184 | ensuring controlled and safe access to the data. 185 | 186 | By following the principles of encapsulation, you can build robust and maintainable code while protecting your data from unintended modifications. 187 | It's a foundational concept in OOP and is widely used in programming to create organized and secure software systems. 188 | 189 | --- 190 | Note the added trailing `_` to the private attributes in the example above. 191 | It is common to add _some_ special decoration to class member attributes in C++ in order to avoid variable shadowing. 192 | That is, to distinguish between local variables and class attributes. Example: 193 | 194 | ```cpp 195 | class Car { 196 | 197 | private: 198 | string model; 199 | 200 | public: 201 | void setModel(std::string model) { 202 | model = model; // what happens here? Answer: We assign the input parameter to itself. NOT what we intended. 203 | // this->model = model; // (1) We fix it by using the special `this` pointer. 204 | // model_ = model; // (2) Better. We solve the issue by decorating the field. 205 | } 206 | }; 207 | ``` 208 | -------------------------------------------------------------------------------- /Chapter3/io_streams.md: -------------------------------------------------------------------------------- 1 | # C++ Input/Output and File Streams 2 | 3 | In C++, Input/Output (I/O) operations play a crucial role in interacting with the user, reading data from external sources, 4 | and writing data to files. C++ provides a comprehensive set of tools for performing these operations, 5 | including standard input and output streams, as well as file streams. 6 | 7 | ## Standard Input and Output Streams 8 | 9 | C++ offers two primary standard I/O streams: 10 | 11 | - **`cin`**: This is the standard input stream, often used for reading data from the user via the keyboard. It's connected to the console by default. 12 | - **`cout`**: This is the standard output stream, used for displaying data to the console. You can use `cout` to print information, results, or messages to the screen. 13 | 14 | Here's a simple example of using `cin` and `cout`: 15 | 16 | ```cpp 17 | #include 18 | 19 | int main() { 20 | int number; 21 | std::cout << "Enter a number: "; 22 | std::cin >> number; 23 | std::cout << "You entered: " << number << std::endl; 24 | return 0; 25 | } 26 | ``` 27 | 28 | In this example, we prompt the user for input using `cout` and read their response into the `number` variable with `cin`. 29 | 30 | ### Writing complex/custom types to `cout` 31 | 32 | Standard datatypes like `int`, `double`, `std::string` and similar can be printed by `cout`. 33 | However, more complex types like `std::vector` and your own classes cannot be represented by default. 34 | To make these work with std::cout, you can: 35 | 36 | 1. Create a function that converts the type in question to a string. 37 | 2. Overload operator `<<`. 38 | 39 | ##### Example 40 | ```cpp 41 | #include 42 | #include 43 | #include 44 | 45 | struct Vector3 { 46 | 47 | float x, y, z; 48 | 49 | std::string toString() const { 50 | return "Vector3(x=" + std::to_string(x) + ", y=" + std::to_string(y) + ", z=" + std::to_string(z) + ")"; 51 | } 52 | 53 | friend std::ostream& operator << (std::ostream& os, const Vector3& v) { 54 | os << "Vector3(x=" << v.x << ", y=" << v.y << ", z=" << v.z << ")"; 55 | return os; 56 | } 57 | }; 58 | 59 | std::string toString(const Vector3& v) { 60 | return "Vector3(x=" + std::to_string(v.x) + ", y=" + std::to_string(v.y) + ", z=" + std::to_string(v.z) + ")"; 61 | } 62 | 63 | int main() 64 | { 65 | Vector3 v; 66 | 67 | std::cout << toString(v) << std::endl; // free function 68 | std::cout << v.toString() << std::endl; // member function 69 | std::cout << v << std::endl; // overloading operator << 70 | 71 | // Prints: 72 | // Vector3(x=0.000000, y=0.000000, z=0.000000) 73 | // Vector3(x=0.000000, y=0.000000, z=0.000000) 74 | // Vector3(x=0, y=0, z=0) 75 | 76 | return 0; 77 | } 78 | ``` 79 | 80 | ## File Streams 81 | 82 | File streams in C++ allow you to perform I/O operations on files. They provide a way to read data from files (input file streams) or write data to files (output file streams). 83 | C++ offers two primary classes for file I/O: 84 | 85 | - **`ifstream`**: This class represents an input file stream and is used for reading data from files. 86 | - **`ofstream`**: This class represents an output file stream and is used for writing data to files. 87 | 88 | To work with file streams, you need to include the `` header. 89 | 90 | Here's an example of using `ifstream` to read data from a file: 91 | 92 | ```cpp 93 | #include 94 | #include 95 | 96 | int main() { 97 | std::ifstream inputFile("example.txt"); 98 | if (inputFile.is_open()) { 99 | std::string line; 100 | while (std::getline(inputFile, line)) { 101 | std::cout << line << std::endl; 102 | } 103 | inputFile.close(); // Would also have been called implicitly due to RAII 104 | } else { 105 | std::cerr << "Unable to open file." << std::endl; 106 | } 107 | return 0; 108 | } 109 | ``` 110 | 111 | In this example, we open a file named "example.txt" for reading using `ifstream`. 112 | We then check if the file is open and proceed to read and display its contents line by line. 113 | 114 | Similarly, you can use `ofstream` to write data to a file. Just replace `ifstream` with `ofstream` and use the `<<` operator to write data to the file. 115 | 116 | C++ provides various methods for manipulating files, including reading and writing binary data. When working with files, 117 | always ensure proper error handling to deal with potential issues like file not found or permission errors. 118 | -------------------------------------------------------------------------------- /Chapter3/raii.md: -------------------------------------------------------------------------------- 1 | # RAII (Resource Acquisition Is Initialization) in C++ 2 | 3 | RAII, which stands for Resource Acquisition Is Initialization, is an essential design principle in C++ that facilitates the management of resources such as memory, files, network connections, and more. 4 | It is a technique that ensures resource cleanup and deallocation in a deterministic and automatic manner, greatly reducing the risk of resource leaks and making C++ programs more robust. 5 | 6 | ## The Core Idea 7 | 8 | At its core, RAII ties the lifetime of a resource directly to the lifetime of an object. 9 | When an object is created, it acquires the resource, and when it goes out of scope or is explicitly destroyed, it automatically releases the resource. 10 | This elegant and deterministic approach guarantees that resources are properly managed without relying on explicit cleanup code. 11 | 12 | ## How RAII Works 13 | 14 | To implement RAII in C++, you typically follow these steps: 15 | 16 | 1. **Resource Acquisition**: When you acquire a resource, such as allocating memory or opening a file, you do it within the constructor of an object. 17 | This constructor is responsible for obtaining and initializing the resource. 18 | 19 | 3. **Resource Release**: You define a destructor for the same object. The destructor's role is to release the acquired resource. 20 | This happens automatically when the object goes out of scope or is explicitly destroyed. 21 | 22 | The following example demonstrates the key principle of RAII: 23 | ```cpp 24 | 25 | class RaiiDemo { 26 | 27 | public: 28 | 29 | // constructor 30 | RaiiDemo() { 31 | // initialize resource when object is created 32 | } 33 | 34 | // destructor 35 | ~RaiiDemo() { 36 | // release resource when object goes out of scope 37 | } 38 | 39 | }; 40 | ``` 41 | 42 | Here's a more elaborate example of RAII for managing a temporary folder: 43 | 44 | ```cpp 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | class TemporaryDirectory { 51 | public: 52 | // Constructor creates a temporary directory 53 | TemporaryDirectory() { 54 | // Generate a unique directory path using C++ filesystem library 55 | directoryPath_ = std::filesystem::temp_directory_path() / std::filesystem::unique_path(); 56 | 57 | // Create the temporary directory 58 | if (!std::filesystem::create_directory(directoryPath_)) { 59 | throw std::runtime_error("Failed to create a temporary directory."); 60 | } 61 | } 62 | 63 | // Member function to get the path of the temporary directory 64 | std::string getPath() const { 65 | return directoryPath_.string(); 66 | } 67 | 68 | // Destructor deletes the temporary directory 69 | ~TemporaryDirectory() { 70 | if (!directoryPath_.empty() && std::filesystem::exists(directoryPath_)) { 71 | std::error_code ec; 72 | std::filesystem::remove_all(directoryPath_, ec); 73 | if (ec) { 74 | std::cerr << "Failed to delete temporary directory: " << ec.message() << std::endl; 75 | } 76 | } 77 | } 78 | 79 | private: 80 | std::filesystem::path directoryPath_; 81 | }; 82 | 83 | int main() { 84 | try { 85 | 86 | { 87 | // Create a TemporaryDirectory object, which creates a temporary directory 88 | TemporaryDirectory tempDir; 89 | 90 | std::cout << "Temporary directory path: " << tempDir.getPath() << std::endl; 91 | 92 | // Use the temporary directory for some operations... 93 | 94 | } // `tempDir` goes out of scope and is destructed (deleting the created directory in the process). 95 | 96 | std::cout << "Temporary directory deleted." << std::endl; 97 | 98 | } catch (const std::exception& e) { 99 | std::cerr << "Error: " << e.what() << std::endl; 100 | return 1; // signal error 101 | } 102 | 103 | return 0; // signal sucess 104 | } 105 | ``` 106 | 107 | ## Benefits of RAII 108 | 109 | RAII offers several advantages: 110 | 111 | * **Simplicity**: It simplifies resource management by encapsulating resource acquisition and release within objects. 112 | * **Safety**: RAII guarantees that resources are properly released, preventing resource leaks and memory errors. 113 | * **Predictability**: The deterministic nature of RAII makes it easier to reason about resource lifetimes in your code. 114 | * **Exception Safety**: RAII naturally handles exceptions; if an exception is thrown during resource acquisition, the destructor will still release the resource. 115 | * **Scope-Based**: Resources are automatically released when they go out of scope, making it suitable for local resources or temporary allocations. 116 | * **Composability**: RAII objects can be easily composed to manage multiple resources in complex scenarios. 117 | 118 | ## Common Uses of RAII 119 | 120 | RAII is commonly applied in various contexts, including: 121 | 122 | * **Memory Management**: Managing dynamic memory with smart pointers (std::unique_ptr, std::shared_ptr) or custom allocators. 123 | * **File Handling**: Automatically closing files with classes like std::ifstream and std::ofstream. 124 | * **Locks and Mutexes**: Ensuring proper acquisition and release of locks using classes like std::lock_guard. 125 | * **Database Connections**: Managing database connections and transactions. 126 | * **Network Resources**: Handling network sockets and connections. 127 | 128 | In conclusion, RAII is a fundamental and powerful technique in C++ for managing resources, offering safety, simplicity, and predictability. 129 | By following the RAII principle, you can write more robust and maintainable C++ code. 130 | -------------------------------------------------------------------------------- /Chapter3/types_refs_ptrs.md: -------------------------------------------------------------------------------- 1 | 2 | # Fundamental Concepts in C++: Value Types, References, Const References, and Pointers 3 | 4 | To become proficient in C++, it's essential to grasp fundamental concepts like value types, references, const references, and pointers. 5 | These concepts lie at the core of C++ programming and play a crucial role in how you work with data and memory. 6 | 7 | ## Value Types 8 | When you create a variable of a value type, it holds the value itself. Here's an example of declaring and using a value type variable: 9 | 10 | ```cpp 11 | int age = 25; // Declaring an integer variable 'age' and initializing it with the value 25 12 | int copyAge = age; // copy 13 | copyAge = 30; // 'age' is still 25 14 | ``` 15 | 16 | Copies of these variables result in independent instances, each with its own data. 17 | Therefore, if you create a copy of a value type variable, changes to one copy won't affect the others. 18 | 19 | > Note: In C++, whenever you return or assign to a value-type, a copy is created. 20 | 21 | ## References 22 | A reference in C++ is an alias or an alternate name for an existing variable. It allows you to access and modify the original variable's value indirectly. 23 | That is, changes made through the reference affect the original data. 24 | References are declared using the `&` symbol and must be initialized when declared. 25 | They are often used as function parameters to avoid copying large objects. Here's an example: 26 | 27 | ```cpp 28 | int age = 42; 29 | int& refAge = age; // 'refAge' is a reference to 'age' 30 | refAge = 10; // Modifying 'age' indirectly through 'refAge'. 31 | ``` 32 | 33 | ## Const References 34 | A const reference in C++ is similar to a regular reference but with the added restriction that you cannot modify the value it references. 35 | It's declared using `const` in front of the reference type. Example: 36 | 37 | ```cpp 38 | int y = 100; 39 | const int& constRefY = y; // 'constRefY' is a constant reference to 'y' 40 | // constRefY = 50; // This would result in a compilation error because you can't modify 'y' through 'constRefY' 41 | ``` 42 | 43 | Const references are useful when you want to pass data to functions without allowing them to change the original value. Here's an example: 44 | 45 | ```cpp 46 | void doWork(const std::vector& data) { 47 | // do something with `data` (we can read, but not modify `data`) 48 | std::vector copy = data; // if I need a copy, I can do that... 49 | } 50 | 51 | int main() { 52 | std::vector data = someFunctionThatReturnsLotsOfNumbers(); 53 | doWork(data); 54 | } 55 | ``` 56 | 57 | ## Pointers 58 | Pointers in C++ are variables that store memory addresses of other variables. They are declared using the `*` symbol. 59 | Pointers can be used to access and manipulate data indirectly. 60 | They are more versatile than references because they can be reassigned to different memory locations. Here's an example: 61 | 62 | ```cpp 63 | int num = 7; 64 | int* ptrNum = # // 'ptrNum' is a pointer to 'num', storing its memory address 65 | *ptrNum = 42; // Modifying 'num' indirectly through 'ptrNum' 66 | ``` 67 | 68 | Pointers and references are similar, however, pointers can be `nullptr`, which means they point to nothing. 69 | References, on the other hand must reference an existing variable. 70 | 71 | ## Summary 72 | 73 | * Value types store the actual value directly. 74 | * References provide an alias for an existing variable. 75 | * Const references allow read-only access to a variable. 76 | * Pointers store memory addresses, offering greater flexibility for memory management and manipulation. 77 | * Given a value-type you can get a reference or a pointer to it that is valid for the duration of the values life-time. 78 | 79 | #### Example 1 80 | ```cpp 81 | int createIntValue() { 82 | return 1; 83 | } 84 | 85 | int createIntRef() { 86 | int value = createIntValue(); 87 | int& ref = *value; 88 | return ref; // bad 89 | } 90 | 91 | int createIntPtr() { 92 | int value = createIntValue(); 93 | int* ptr = &value; 94 | return ptr; // bad 95 | } 96 | 97 | int main() { 98 | int i1 = createIntValue(); // Safe. Function returns a new copy. 99 | int& i2 = createIntRef(); // Undefined beheviour. The underlying value no longer exist. 100 | int* i3 = createIntPtr(); // Undefined beheviour. The underlying value no longer exist. 101 | } 102 | ``` 103 | 104 | #### Example 2 105 | ```cpp 106 | 107 | class Demo { 108 | 109 | public: 110 | int getValue() const { 111 | return value; 112 | } 113 | 114 | int& getValueRef() const { 115 | return *value; 116 | } 117 | 118 | int* getValuePtr() const { 119 | return &value; 120 | } 121 | 122 | private: 123 | int value = 0; 124 | }; 125 | 126 | int main() { 127 | 128 | Demo obj; 129 | 130 | // All these are fine as obj is still alive and well. 131 | int i1 = obj.getValue(); 132 | int& i2 = obj.getValueRef(); 133 | int* i3 = obj.getValuePtr(); 134 | 135 | // Note that i2, and i3 provides access to the underlying private member. 136 | // We are then able to change the value of the value held be `obj`, thus breaking encapsulation! 137 | 138 | } 139 | ``` 140 | 141 | Understanding these concepts is fundamental to mastering C++ programming. 142 | 143 | # Choosing the Right Data Passing Mechanism 144 | 145 | Choosing between value types, references, const references, and pointers when passing data in C++ depends on several factors, including the desired behavior, memory efficiency, and the potential for data modification. 146 | Here are some guidelines to help you make the right choice: 147 | 148 | ## Value Types 149 | 150 | * Use value types when you want to work with independent copies of data. 151 | * Use them for small, simple data types where copying is not a significant performance concern. 152 | * Choose value types when you don't want changes to the passed data to affect the original data. 153 | 154 | ```cpp 155 | void processValue(int x) { 156 | // 'x' is a copy of the original value 157 | // Changes to 'x' won't affect the original data 158 | x += 10; 159 | } 160 | ``` 161 | ## References 162 | 163 | * Use references when you want to work with the original data and potentially modify it. 164 | * Use references when passing large objects or structures to avoid the overhead of copying. 165 | * Be cautious with references to ensure you don't inadvertently modify data you didn't intend to. 166 | 167 | ```cpp 168 | void modifyReference(int& x) { 169 | // 'x' is a reference to the original data 170 | // Changes to 'x' will affect the original data 171 | x += 10; 172 | } 173 | ``` 174 | 175 | ## Const References 176 | 177 | * Use const references when you want to work with the original data but ensure that it remains unchanged. 178 | * Employ them when passing large objects or structures to avoid copying and enforce read-only access. 179 | * Const references are commonly used for function parameters that don't need to modify the input. 180 | 181 | ```cpp 182 | void readData(const int& x) { 183 | // 'x' is a const reference to the original data 184 | // You can't modify 'x' within this function 185 | // Suitable for reading data without changing it 186 | } 187 | ``` 188 | 189 | ### Pointers 190 | 191 | * Use pointers when you need to work with memory addresses directly. 192 | * Choose pointers for more advanced memory management tasks, like dynamic memory allocation (e.g., with new and delete). 193 | * Be mindful of the potential for null pointers and memory leaks when using pointers. 194 | 195 | ```cpp 196 | void modifyViaPointer(int* ptr) { 197 | // 'ptr' is a pointer to the original data 198 | // You can modify the data through 'ptr' 199 | (*ptr) += 10; 200 | } 201 | ``` 202 | 203 | 204 | -------------------------------------------------------------------------------- /Chapter4/memory.md: -------------------------------------------------------------------------------- 1 | # Memory managment in C++ 2 | 3 | In C++, memory management is a crucial aspect of programming, and understanding the concepts of the stack and heap is fundamental to writing efficient and reliable code. 4 | These two memory areas play distinct roles in managing data during program execution. Let's explore the stack and heap in C++. 5 | 6 | ## Stack 7 | The stack is a region of memory used for the management of function calls and local variables. It operates in a Last-In, First-Out (LIFO) manner, meaning that the most recently called function is at the top of the stack, and it must finish execution before the previous function can resume. The stack is relatively small and typically has a fixed size determined by the system or the compiler. 8 | 9 | Key characteristics of the stack: 10 | 11 | * **Function Call Management:** Every time a function is called, a new stack frame is created to store its local variables, function arguments, and return address. When the function returns, its stack frame is destroyed. 12 | 13 | * **Automatic Memory Management:** Memory for variables on the stack is allocated and deallocated automatically. When a variable goes out of scope (e.g., when a function exits), the memory associated with it is immediately reclaimed. 14 | 15 | * **Fast Access:** Accessing data on the stack is faster than on the heap because it involves simple pointer manipulation. 16 | 17 | * **Limited Size:** The stack has a limited size, and if it becomes exhausted (usually due to excessive function calls or large local variables), it can lead to a stack overflow error. 18 | 19 | * **No Need for Explicit Deallocation:** You do not need to explicitly release memory on the stack; it is managed by the system. 20 | 21 | ```cpp 22 | #include 23 | 24 | int main() { 25 | int stackVariable = 42; // Stack allocation 26 | 27 | std::cout << "Stack Variable: " << stackVariable << std::endl; 28 | 29 | // Memory for stackVariable is automatically released when it goes out of scope 30 | 31 | return 0; 32 | } 33 | ``` 34 | 35 | In this example, `stackVariable` is allocated on the stack, and its memory is automatically released when the main function exits. 36 | 37 | ## Heap 38 | The heap is a region of memory used for dynamic memory allocation, where you can request memory at runtime and release it when you're done with it. Unlike the stack, the heap's memory allocation and deallocation are explicit and are controlled by the programmer. 39 | 40 | Key characteristics of the heap: 41 | 42 | * **Dynamic Memory Allocation:** Memory on the heap is allocated and deallocated explicitly using functions like `new` and `delete` (or `malloc()` and `free()` in C). This allows you to allocate memory at runtime, making it suitable for data structures with variable sizes. 43 | 44 | * **Lack of Automatic Management:** Memory on the heap must be manually managed. Failure to deallocate memory when it's no longer needed can lead to memory leaks. 45 | 46 | * **Large and Flexible:** The heap is typically much larger than the stack and can grow dynamically, depending on system memory availability. 47 | 48 | * **Slower Access:** Accessing data on the heap is slower than on the stack because it involves pointer dereferencing and may require traversing data structures. 49 | 50 | ```cpp 51 | #include 52 | 53 | int main() { 54 | int* heapVariable = new int(42); // Heap allocation 55 | 56 | std::cout << "Heap Variable: " << *heapVariable << std::endl; 57 | 58 | // Manually deallocate memory 59 | delete heapVariable; // Note: This is necessary to prevent memory leaks 60 | 61 | return 0; 62 | } 63 | ``` 64 | 65 | In this example, `heapVariable` is allocated on the heap using new, and it's essential to deallocate the memory using delete to prevent memory leaks. 66 | 67 | In C++, you have the freedom to choose between the stack and heap for storing data, depending on your program's requirements. It's essential to use each memory area appropriately to avoid memory-related issues, such as stack overflows or memory leaks, and to ensure efficient resource utilization in your C++ programs. 68 | 69 | You can also allocate objects on the stack that contain pointers to heap-allocated objects. 70 | 71 | ```cpp 72 | #include 73 | 74 | class MyClass { 75 | public: 76 | MyClass(int value) : data(new int(value)) {} 77 | ~MyClass() { delete data; } 78 | 79 | int getValue() const { return *data; } 80 | 81 | private: 82 | int* data; 83 | }; 84 | 85 | int main() { 86 | MyClass stackObject(42); // Stack allocation with heap-allocated data 87 | 88 | std::cout << "Stack Object Value: " << stackObject.getValue() << std::endl; 89 | 90 | // Memory for stackObject is automatically released when it goes out of scope 91 | 92 | return 0; 93 | } 94 | ``` 95 | 96 | In this example, `stackObject` is allocated on the stack, but it contains a pointer (data) to an integer allocated on the heap. The destructor of `MyClass` is responsible for deallocating the heap-allocated memory when `stackObject`goes out of scope (RAII). 97 | 98 | Remember that proper memory management is crucial when dealing with heap-allocated memory to avoid memory leaks and undefined behavior. In modern C++, using smart pointers (e.g., std::unique_ptr and std::shared_ptr) is recommended to simplify and improve memory management in heap-allocated objects. 99 | 100 | ## Smart Pointers in Modern C++: Enhancing Memory Management and Safety 101 | 102 | In modern C++, memory management and safety are paramount concerns. Traditional C++ provided manual memory management through raw pointers, which often led to memory leaks, dangling pointers, and other memory-related issues. To address these problems, C++ introduced smart pointers, which have become a cornerstone of modern C++ programming. This section will delve into what smart pointers are and why they should be used in contemporary C++ development. 103 | 104 | ### What are Smart Pointers? 105 | 106 | Smart pointers are objects that wrap around raw pointers and provide automatic memory management capabilities. They are part of the C++ Standard Library and come in two main flavors: `std::shared_ptr` and `std::unique_ptr`, introduced in C++11, and `std::weak_ptr`, introduced in the same standard but serving a different purpose. These smart pointers help mitigate many of the common pitfalls associated with manual memory management. 107 | 108 | ### Why Use Smart Pointers in Modern C++? 109 | 110 | * **Automatic Memory Management:** Smart pointers automatically manage the memory they point to. When the smart pointer goes out of scope or is no longer needed, it automatically deallocates the memory, eliminating the risk of memory leaks. This feature reduces the cognitive burden on developers and minimizes the chances of human error in memory management. 111 | 112 | * **Resource Ownership Clarification:** Smart pointers make it clear which parts of the code are responsible for owning and managing resources. For example, std::unique_ptr signifies exclusive ownership, while std::shared_ptr indicates shared ownership. This clarity enhances code readability and maintainability. 113 | 114 | * **Reduced Dangling Pointers:** Dangling pointers, which point to memory that has been deallocated, can lead to undefined behavior. Smart pointers help prevent this issue by automatically nullifying themselves when the pointed-to memory is deallocated. 115 | 116 | * **Exception Safety:** Smart pointers enhance exception safety in C++ programs. If an exception is thrown, smart pointers ensure that any dynamically allocated resources are properly released as the stack unwinds. This prevents resource leaks and helps maintain program integrity. 117 | 118 | * **Simpler Code:** Smart pointers often lead to cleaner and more concise code. They eliminate the need for explicit new and delete calls, reducing boilerplate code and making the codebase less error-prone. 119 | 120 | * **Memory Leak Prevention:** Smart pointers are effective tools for preventing memory leaks, even in complex scenarios where objects are shared among multiple parts of the code. They manage reference counts and ensure that memory is released when the last reference to it is gone. 121 | 122 | * **Interoperability:** Smart pointers can be used in conjunction with other C++ features like containers and algorithms, making them an integral part of modern C++ idioms. 123 | 124 | While smart pointers offer numerous advantages, it's important to choose the appropriate type of smart pointer for a given situation. `std::unique_ptr` should be used when ownership is strictly exclusive, while `std::shared_ptr` is suitable for shared ownership scenarios. 125 | `std::weak_ptr` complements std::shared_ptr by breaking circular references and avoiding memory leaks. 126 | 127 | > A circular reference, also known as a circular dependency or reference cycle, is a situation in computer programming and data structures where two or more objects or elements reference each other in a way that creates an infinite loop or cycle of references. 128 | 129 | In conclusion, smart pointers have revolutionized memory management in modern C++ by providing automatic memory management, enhancing code safety, and simplifying complex resource ownership scenarios. They are essential tools for writing reliable and maintainable C++ code. Developers are encouraged to adopt smart pointers as a best practice to make their code safer, more efficient, and less error-prone. 130 | -------------------------------------------------------------------------------- /Chapter4/move.md: -------------------------------------------------------------------------------- 1 | # Move semantics 2 | 3 | Move semantics is a feature introduced in C++11 that allows you to efficiently transfer the ownership of resources from one object to another without unnecessary copying. 4 | This can lead to significant performance improvements in certain situations where copying large amounts of data can be expensive. 5 | 6 | 7 | ```cpp 8 | #include 9 | #include 10 | 11 | int main() { 12 | std::string source = "Hello, World!"; // Create a string 13 | std::string destination = source; // Copy the string to another variable 14 | 15 | // Output both strings 16 | std::cout << "Source: " << source << std::endl; 17 | std::cout << "Destination: " << destination << std::endl; 18 | 19 | return 0; 20 | } 21 | ``` 22 | 23 | In this example, we create a string named `source` and then copy its content to another string called `destination`. 24 | This copying process can be inefficient, especially if the string is large because it duplicates the data, consuming more memory and time. 25 | 26 | Now, let's use move semantics to make this more efficient: 27 | 28 | ```cpp 29 | #include 30 | #include 31 | #include //std::move 32 | 33 | int main() { 34 | std::string source = "Hello, World!"; // Create a string 35 | std::string destination = std::move(source); // Move the string to another variable 36 | 37 | // Output both strings 38 | std::cout << "Source: " << source << std::endl; 39 | std::cout << "Destination: " << destination << std::endl; 40 | 41 | return 0; 42 | } 43 | ``` 44 | 45 | In this updated example, we use `std::move` to _transfer_ the contents of `source` to `destination`. This means we're not making a copy; instead, we're essentially saying, "You, destination, take ownership of what's in source," which is much faster and uses less memory. 46 | After the move, `source` is still there, but its content is left in an unspecified (but valid) state (usually, it's an empty string in the case of strings). Meanwhile, `destination` has the original content. 47 | 48 | ### Summary 49 | 50 | Move semantics allows us to efficiently hand over resources (like strings or memory) from one object to another without making unnecessary copies, which can be faster and more memory-efficient. 51 | It's like passing a toy car from one child to another without making an extra copy of the car. 52 | 53 | Move semantics are particularly useful when working with containers like std::vector or when returning objects from functions, as they can help minimize unnecessary copying and improve performance. 54 | However, it's important to be aware of the potential pitfalls, like accessing moved-from objects, to use move semantics effectively in C++. 55 | -------------------------------------------------------------------------------- /Chapter4/polymorphism.md: -------------------------------------------------------------------------------- 1 | # Polymorphism 2 | 3 | Polymorphism means many forms, and is one of the fundamental principles of Object-Oriented Programming (OOP). 4 | It allows objects of different classes to be treated as objects of a common superclass. 5 | It promotes flexibility and reusability in code, enabling you to write more generic 6 | and extensible programs. 7 | 8 | In C++ we have two types of polymorphism: 9 | 10 | 1. Compile Time Polymorphism 11 | 2. Runtime Polymorphism 12 | 13 | ## Compile-time polymorphism 14 | Compile-time polymorphism is done by overloading an operator or function. 15 | 16 | ### Function overloading 17 | 18 | Function overloading allows you to define multiple functions with the same name in the 19 | same scope but with different parameters. 20 | The appropriate function is called based on the number and types of 21 | arguments passed during the function call. 22 | 23 | ```cpp 24 | class Example { 25 | public: 26 | void display(int num) { 27 | cout << "Integer: " << num << endl; 28 | } 29 | 30 | void display(double num) { 31 | cout << "Double: " << num << endl; 32 | } 33 | }; 34 | 35 | int main() { 36 | Example obj; 37 | obj.display(5); // Output: Integer: 5 38 | obj.display(3.14); // Output: Double: 3.14 39 | return 0; 40 | } 41 | ``` 42 | 43 | #### Operator Overloading 44 | 45 | Operator overloading allows you to define how operators behave for user-defined data types. 46 | It enables you to redefine the behavior of operators like `+`, `-`, `*`, etc., 47 | for objects of a class. I.e., operators can behave differently depending on the type used with them. 48 | 49 | ```cpp 50 | class Complex { 51 | 52 | public: 53 | 54 | // constructor with default values 55 | Complex(int real = 0, complex = 0) 56 | : real_(real), complex_(complex){} 57 | 58 | // overloading operator + 59 | Complex operator + (const Complex &obj) { 60 | Complex temp; 61 | temp.real = real + obj.real; 62 | temp.imag = imag + obj.imag; 63 | return temp; 64 | } 65 | 66 | private: 67 | int real_, imag_; 68 | }; 69 | 70 | int main() { 71 | Complex num1, num2; // Assume num1 and num2 are initialized with meaningful values 72 | Complex result = num1 + num2; // Operator + is overloaded for objects of Complex class 73 | return 0; 74 | } 75 | 76 | ``` 77 | 78 | ## Runtime polymorphism 79 | 80 | Run-time polymorphism in C++ is achieved through virtual functions and inheritance. 81 | 82 | ### Inheritance 83 | 84 | Inheritance is a fundamental object-oriented programming concept that allows you to create a new class based on an existing class. The new class, known as the derived class, inherits properties and behaviors (data members and member functions) from the existing class, called the base class. This facilitates code reuse and the creation of a hierarchy of classes. 85 | 86 | ```cpp 87 | class BaseClass { 88 | // members and methods of BaseClass 89 | }; 90 | 91 | class DerivedClass : access-specifier BaseClass { 92 | // members and methods of DerivedClass 93 | }; 94 | ``` 95 | 96 | - `access-specifier` can be one of three: `public`, `protected`, or `private`. It specifies the access level for the members inherited from the base class. 97 | 98 | ##### Example 99 | 100 | ```cpp 101 | class Animal { 102 | public: 103 | void eat() { 104 | cout << "Animal is eating." << endl; 105 | } 106 | }; 107 | 108 | class Dog : public Animal { 109 | public: 110 | void bark() { 111 | cout << "Dog is barking." << endl; 112 | } 113 | }; 114 | ``` 115 | 116 | In this example, `Dog` is a derived class inheriting publicly from `Animal`. Now, objects of the `Dog` class can access the `eat()` method from the `Animal` class as well as its own `bark()` method. 117 | 118 | ```cpp 119 | int main() { 120 | Dog myDog; 121 | myDog.eat(); // Output: Animal is eating. 122 | myDog.bark(); // Output: Dog is barking. 123 | return 0; 124 | } 125 | ``` 126 | 127 | This way, inheritance allows for creating a hierarchy of classes, enabling the creation of more specialized classes based on existing ones, promoting code reuse and modularity. 128 | 129 | ### Virtual Functions 130 | In C++, you use the `virtual`` keyword to declare a member function of the base class as virtual. 131 | Virtual functions are resolved at runtime, allowing the appropriate derived class function to be called based on the 132 | object's actual type rather than the declared type. Virtual functions can have a defualt implementation that gets inherited (and possibly overriden (replaced)) or be defined as pure virtual where some subclass __must__ provide an implementation. 133 | 134 | ```cpp 135 | class Shape { 136 | public: 137 | virtual void draw() = 0; //pure virtual 138 | }; 139 | 140 | class Circle : public Shape { 141 | public: 142 | void draw() override { 143 | cout << "Drawing a circle\n"; 144 | } 145 | }; 146 | 147 | class Square : public Shape { 148 | public: 149 | void draw() override { 150 | cout << "Drawing a square\n"; 151 | } 152 | }; 153 | 154 | ``` 155 | 156 | In this example, the `draw()` function is declared as `virtual` in the Shape class. 157 | When you have a pointer or reference of the base class type pointing to 158 | an object of a derived class, the correct `draw()` function is called based on the actual object type. 159 | 160 | ```cpp 161 | 162 | int main() { 163 | std::unique_ptr shape1 = std::make_unique(); 164 | std::unique_ptr shape2 = std::make_unique(); 165 | 166 | shape1->draw(); // Output: Drawing a circle 167 | shape2->draw(); // Output: Drawing a square 168 | 169 | return 0; 170 | } 171 | 172 | ``` 173 | 174 | In this example, polymorphism allows the `draw()` function to behave differently based on the actual type of the object it's called on. 175 | 176 | Polymorphism enables you to write more generic and flexible code, making it easier to extend and modify your programs without altering existing code. 177 | It's a powerful concept that enhances the maintainability and readability of object-oriented programs in C++. 178 | 179 | #### Object slicing 180 | 181 | Object slicing in C++ occurs when you assign an object of a derived class to an object of its base class type. In this situation, if the derived class object contains additional member variables or member functions that are not present in the base class, those extra parts of the object are "sliced off." 182 | This can lead to unexpected behavior and loss of data if you are not careful. 183 | 184 | It's important to be aware of object slicing when dealing with inheritance 185 | and assignments between objects of base and derived classes. 186 | To avoid object slicing, you can use pointers or references to the base class 187 | when working with polymorphic behavior, as shown in the previous examples with virtual functions. 188 | Pointers or references allow you to access the derived class's specific members without losing data due to object slicing. 189 | 190 | --- 191 | ### More complex example using runtime-polymorphism 192 | 193 | Imagine we have an application where we want to have some 194 | logging for debugging or informational purposes. 195 | 196 | We could place some `std:cout` calls in our code, but that is not very flexible and would be alot of work to change later. 197 | What if we wanted to log to a file rather than to the terminal? Let us use inheritance! 198 | 199 | We can define an abstract base class `Logger` that declares a (pure) virtual 200 | function `log(args` like so: 201 | 202 | ```cpp 203 | class Logger { 204 | public: 205 | virtual void log(const std::string& str) = 0; 206 | }; 207 | ``` 208 | 209 | As `Logger` defines a pure virtual function, it is an abstract type, 210 | meaning it is not a complete type that we can instantiate. 211 | We need some other class(es) to subclass it! 212 | 213 | Let us create a `FileLogger` and a `ConsoleLogger` like so: 214 | 215 | ```cpp 216 | class FileLogger: public Logger { 217 | 218 | public: 219 | FileLogger(const std::filesystem::path& outFile) 220 | : out_(outFile) {} 221 | 222 | void log(const std::string& str) override { 223 | out_ << str << std::endl; 224 | } 225 | 226 | private: 227 | std::ofstream out_; 228 | }; 229 | 230 | class ConsoleLogger: public Logger { 231 | 232 | public: 233 | 234 | void log(const std::string& str) { 235 | std::cout << str << std::endl; 236 | } 237 | 238 | }; 239 | ``` 240 | 241 | The `FileLogger` and `ConsoleLogger` both inherits from `Logger` 242 | and provides an implementation of the `log(...)` function. 243 | 244 | Now we can write code that only knows about `Logger` and it's up to the user to 245 | decide whether to log to file or console as shown here: 246 | 247 | ```cpp 248 | class Simulation { 249 | 250 | public: 251 | void stepSimulation(double dt) { 252 | // simulate something 253 | 254 | if (logger_) { 255 | logger_->log("Stepping simulation, t=" + std::to_string(t)); 256 | } 257 | 258 | t += dt; 259 | } 260 | 261 | void setLogger(std::unique_ptr logger) { 262 | logger_ = std::move(logger); 263 | } 264 | 265 | private: 266 | double t = 0; 267 | // note that the type is the abstract Logger type 268 | std::unique_ptr logger_; 269 | }; 270 | 271 | int main() { 272 | 273 | Simulation sim; 274 | auto logger = std::make_unique("logfile.txt"); 275 | // auto logger = std::make_unique(); // could use this one as well 276 | 277 | sim.setLogger(std::move(logger)); 278 | 279 | sim.stepSimulation(); 280 | } 281 | ``` 282 | 283 | Now we can remove or add new `Logger` implementations without `Simulation` even knowing! 284 | -------------------------------------------------------------------------------- /Chapter4/templates.md: -------------------------------------------------------------------------------- 1 | # Templates 2 | 3 | In C++, templates are a powerful feature that allows you to write generic programs. Templates enable you to create functions and classes that work with any data type, providing a way to create highly flexible and reusable code. 4 | They are a fundamental part of C++'s support for generic programming, allowing you to write algorithms and data structures that can work with different types without having to rewrite the code for each specific type. 5 | 6 | ## Understanding Templates 7 | 8 | A template is a blueprint or a formula for creating generic functions or classes. 9 | It allows you to define a placeholder for the data type or class that will be used later. 10 | When you use a template, you can substitute the placeholder with any data type, allowing you to create functions or classes that work with various types. 11 | 12 | ### Syntax of Templates 13 | 14 | The basic syntax for creating a function template in C++ looks like this: 15 | 16 | ```cpp 17 | template 18 | T functionName(T parameter) { 19 | // Function body 20 | } 21 | ``` 22 | 23 | In the above syntax: 24 | 25 | - `template `: This line declares a template with a placeholder `T` representing the data type. 26 | - `T functionName(T parameter)`: This line declares a function named functionName that takes a parameter of type `T`. 27 | 28 | Similarly, you can create class templates: 29 | 30 | ```cpp 31 | template 32 | class ClassName { 33 | // Class body 34 | }; 35 | ``` 36 | 37 | In C++, the choice of the template parameter, often denoted as `T`, is a convention rather than a strict rule. The letter `T` stands for "Type" and is commonly used as a placeholder to represent any data type that can be passed to the template. 38 | However, it's essential to understand that you can use any valid C++ identifier as a template parameter name. For example, instead of `T`, you could use `Type`, `ElementType`, or any other meaningful name that helps make your code more readable and understandable. 39 | 40 | However, the convention of using `T` is widespread in the C++ community. It's recognizable and serves as a clear indicator that the identifier is a placeholder for a data type. 41 | Ultimately, the choice of the template parameter name is a matter of readability, maintainability, and personal or team preference. 42 | Just ensure that whatever name you choose, it clearly conveys the purpose of the template parameter in your code. 43 | 44 | ### Using Templates 45 | 46 | To use a template, you specify the data type or class that you want to substitute for the template parameter when calling the function or instantiating the class. For example: 47 | 48 | ```cpp 49 | #include 50 | 51 | template 52 | T add(T a, T b) { 53 | return a + b; 54 | } 55 | 56 | int main() { 57 | int sum_int = add(5, 10); // Uses the add(5, 10) function 58 | double sum_double = add(3.5, 2.7); // Uses the add(3.5, 2.7) function 59 | 60 | std::cout << "Sum of integers: " << sum_int << std::endl; 61 | std::cout << "Sum of doubles: " << sum_double << std::endl; 62 | 63 | return 0; 64 | } 65 | ``` 66 | 67 | In the above example, the `add` function template is used with both `int` and `double` data types. 68 | 69 | ## Advantages of Templates 70 | 71 | 1. **Reusability:** Templates allow you to write generic code that can be reused with different data types, promoting code reusability. 72 | 73 | 2. **Performance:** Templates in C++ are resolved at compile-time, which means there is no runtime overhead associated with using templates. The compiler generates specialized versions of the template functions or classes for each data type, optimizing performance. 74 | 75 | 3. **Type Safety:** Templates provide strong type checking at compile-time, ensuring type safety without sacrificing flexibility. 76 | 77 | In summary, templates in C++ provide a powerful mechanism for creating generic functions and classes, allowing you to write flexible, efficient, and type-safe code. 78 | By leveraging templates, you can enhance the flexibility and reusability of your C++ programs. 79 | -------------------------------------------------------------------------------- /Chapter5/observer.md: -------------------------------------------------------------------------------- 1 | # Observer Pattern 2 | 3 | The Observer pattern is a behavioral design pattern where an object, known as the subject, 4 | maintains a list of dependents, called observers, that are notified of any changes in the subject's state. 5 | This pattern establishes a one-to-many relationship between objects, where multiple observers can listen to and respond to changes in the subject without being tightly coupled to it. 6 | This decoupling promotes flexibility and reusability in the design of software systems. 7 | 8 | The Observer pattern allows for dynamic relationships between objects. Observers can be added or removed at runtime, enabling flexibility in how objects collaborate. This pattern is widely used in various scenarios, 9 | including implementing graphical user interfaces, event handling systems, and distributed systems. 10 | 11 | ### Example 12 | 13 | ```cpp 14 | 15 | #include 16 | #include 17 | 18 | class Observer; 19 | 20 | // Subject interface that defines methods for attaching, detaching, and notifying observers. 21 | class Subject { 22 | public: 23 | virtual ~Subject() = default; 24 | virtual void attach(Observer* observer) = 0; 25 | virtual void detach(Observer* observer) = 0; 26 | virtual void notify() = 0; 27 | }; 28 | 29 | // Concrete subject class that implements the Subject interface. 30 | class ConcreteSubject : public Subject { 31 | public: 32 | void attach(Observer* observer) override { 33 | observers.emplace_back(observer); 34 | } 35 | 36 | void detach(Observer* observer) override { 37 | // Implementation for detaching an observer. 38 | } 39 | 40 | void setState(int state) { 41 | this->state = state; 42 | notify(); // Notify observers when the state changes. 43 | } 44 | 45 | int getState() const { 46 | return state; 47 | } 48 | 49 | void notify() override { 50 | for (Observer* observer : observers) { 51 | observer->update(); 52 | } 53 | } 54 | 55 | private: 56 | std::vector observers; 57 | int state; 58 | }; 59 | 60 | // Observer interface that defines the update method to be called by the subject. 61 | class Observer { 62 | public: 63 | virtual ~Observer() = default; 64 | virtual void update() = 0; 65 | }; 66 | 67 | // Concrete observer class that implements the Observer interface. 68 | class ConcreteObserver : public Observer { 69 | public: 70 | ConcreteObserver(ConcreteSubject* subject) : subject(subject) { 71 | subject->attach(this); 72 | } 73 | 74 | void update() override { 75 | std::cout << "Observer received update. New state: " << subject->getState() << std::endl; 76 | } 77 | 78 | private: 79 | ConcreteSubject* subject; 80 | }; 81 | 82 | int main() { 83 | ConcreteSubject subject; 84 | 85 | ConcreteObserver observer1(&subject); 86 | ConcreteObserver observer2(&subject); 87 | 88 | // Change the state of the subject, which triggers the notification to observers. 89 | subject.setState(42); 90 | 91 | return 0; 92 | } 93 | 94 | ``` 95 | 96 | In this example, `Subject` is an interface that declares methods for attaching, 97 | detaching, and notifying observers. ConcreteSubject is a class implementing the Subject interface. 98 | It maintains a list of observers and notifies them when its state changes. 99 | 100 | `Observer` is an interface with an `update` method that concrete observers must implement. 101 | `ConcreteObserver` is a class implementing the `Observer`. It registers itself with a 102 | `ConcreteSubject` instance and receives updates when the subject's state changes. 103 | 104 | In the main function, a `ConcreteSubject` instance is created, and two `ConcreteObserver` 105 | instances are attached to it. When the subject's state changes using `subject.setState(42)`, 106 | both observers are notified, and they print the new state. 107 | -------------------------------------------------------------------------------- /Chapter5/soc.md: -------------------------------------------------------------------------------- 1 | # Separation of Concerns (SoC) in Software Development 2 | 3 | Separation of Concerns (SoC) is a fundamental design principle in software engineering that 4 | advocates breaking a computer program into distinct features or concerns, 5 | each of which addresses a separate set of functionalities. 6 | The main idea behind SoC is to divide the software system into smaller, 7 | manageable parts, making it easier to develop, maintain, and understand. 8 | 9 | Follwing is some of the key aspects of SoC: 10 | 11 | - **Modularity:** 12 | SoC promotes modularity, which means breaking down a system into smaller, self-contained modules. 13 | Each module is responsible for a specific aspect of the functionality. These modules can be developed, tested, 14 | modified, and maintained independently, leading to greater flexibility and reusability of code. 15 | 16 | - **Encapsulation:** 17 | Encapsulation is a concept in object-oriented programming where the internal workings of a 18 | module are hidden from the rest of the system. By encapsulating data and behavior within modules, 19 | you can control access to the module's internal state, allowing changes to be made without affecting 20 | the rest of the system. This encapsulation enhances security and simplifies maintenance. 21 | 22 | - **Single Responsibility Principle (SRP):** 23 | SRP, one of the SOLID principles of object-oriented design, is closely related to SoC. 24 | It states that a class should have only one reason to change, meaning that it should have only 25 | one job or responsibility within the system. By adhering to SRP, developers ensure that each module 26 | or class focuses on a specific functionality, enhancing clarity and maintainability. 27 | 28 | - **Ease of Maintenance:** 29 | SoC simplifies the debugging and maintenance process. When concerns are separated, 30 | it's easier to identify and fix issues within a specific module without disrupting the entire system. 31 | Developers can work on one concern at a time, which improves productivity and reduces the risk of introducing new bugs. 32 | 33 | - **Interoperability and Reusability** 34 | Separating concerns enables components to be reused in different contexts. 35 | A module designed to handle a specific concern can be utilized in various projects without 36 | modification if its interface remains consistent. This reusability promotes the development of robust, 37 | tested components that can be integrated into different systems. 38 | 39 | - **Collaborative Development:** 40 | SoC allows multiple developers or teams to work on different concerns simultaneously. 41 | This parallel development accelerates the overall development process, especially in large and complex projects. 42 | Teams can focus on their specific areas of expertise without interfering with other parts of the system. 43 | 44 | 45 | ### Achieving Separation of Concerns 46 | Achieving Separation of Concerns in software development involves applying various design principles and techniques to structure your codebase. 47 | Here are several strategies you can use to achieve this separation: 48 | 49 | - **Modular Programming:** 50 | Break down your software into smaller modules, each responsible for a specific functionality. Modules should have well-defined interfaces, allowing them to interact with other modules in a controlled manner. 51 | 52 | - **Use of Functions and Methods:** 53 | Encapsulate specific tasks into functions (in procedural programming) or methods (in object-oriented programming). Functions/methods should ideally perform one task and do it well, adhering to the Single Responsibility Principle. 54 | 55 | - **Object-Oriented Design:** 56 | Utilize classes and objects to encapsulate data and behavior. Classes represent specific concerns or entities in your system, encapsulating related data and methods. Properly designed classes enhance modularity and encapsulation. 57 | 58 | - **Design Patterns:** 59 | Familiarize yourself with design patterns such as the Observer pattern, Strategy pattern, and Factory pattern. These patterns provide tested solutions to common design problems and often promote separation of concerns in their implementations. 60 | 61 | - **Event-Driven Architecture:** 62 | Implement an event-driven architecture where components communicate through events or messages. This promotes loose coupling between components, allowing them to operate independently and handle specific concerns. 63 | 64 | - **Separate User Interface (UI) from Business Logic:** 65 | In applications with graphical user interfaces, separate the UI code from the underlying business logic. Use design patterns like Model-View-Controller (MVC) to achieve this separation, ensuring that the presentation layer is decoupled from the application's core logic. 66 | 67 | - **Testing and Test Automation:** 68 | Write unit tests and automated tests that focus on specific concerns. Test each module or component in isolation to ensure they work as expected. Automated tests also act as a safety net when you make changes, ensuring that existing concerns are not affected. 69 | 70 | 71 | ## Summary 72 | In summary, Separation of Concerns is a foundational principle that promotes a systematic approach to software development. By organizing code into modular, encapsulated components, developers can create flexible, maintainable, and scalable software systems. This approach enhances collaboration, facilitates debugging, 73 | and ultimately leads to the creation of more reliable and efficient software applications. 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 AIS1003-V-2023 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AIS1003 E-book 2 | 3 | Welcome to this E-book for the course [AIS1003](https://www.ntnu.no/studier/emner/AIS1003#tab=omEmnet). 4 | 5 | The book will be updated regularly throughout the semester. Make sure to bookmark it! 6 | 7 | > Note. The book is in an early state of development. Consider it a preview. 8 | 9 | ## Content 10 | 11 | | Week | Topic | 12 | |------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 13 | | 34 |
  • [Introduction](Chapter1/introduction.md)
    • [Basic Structure](Chapter1/basic_structure.md)
    • [Control Statements](Chapter1/control_statements.md)
    • [Variables and Basic Types](Chapter1/variables.md)
    • [Functions](Chapter1/functions.md)
    • [Operators and Expressions](Chapter1/operators_expressions.md)
  • [Getting Started](getting_started.md)
| 14 | | 35 |
  • [CMake introduction](Chapter2/cmake_intro.md)
  • [Version Control & Git](Chapter2/version_control.md)
  • [PlatformIO](Chapter2/platformio.md)
| 15 | | 36 |
  • [C++ Standard Library](Chapter2/standard_library.md)
  • [Data structures](Chapter2/data_structures.md)
| | | 16 | | 37,38 |
  • [Classes](Chapter3/classes.md)
    • [RAII](Chapter3/raii.md)
  • [Values, References & Pointers](Chapter3/types_refs_ptrs.md)
  • [IO & Streams](Chapter3/io_streams.md)
| | 17 | | 39,40 |
  • [Memory managment](Chapter4/memory.md)
  • [Move semantics](Chapter4/move.md)
| 18 | | 41,42 |
  • [Polymorphism](Chapter4/polymorphism.md)
  • [Templates](Chapter4/templates.md)
| | 19 | | 43 |
  • [Separation of Concerns](Chapter5/soc.md)
  • [Observer Pattern](Chapter5/observer.md)
| 20 | 21 | ### Additional resources 22 | 23 | While aimed to support your endeavours, this book should not be treated as a complete reference guide. It is crucial that you supplement with other resources. 24 | Below is a non-exhaustive list of recommended learning resources. 25 | 26 | #### Books 27 | - __C++ Primer, Fifth Edition__ - A comprehensive and widely-used introductory book on C++ programming, covering essential concepts and techniques for beginners. 28 | - __A Tour of C++__ - A concise and high-level overview of modern C++ features, focusing on important language elements and programming principles for intermediate programmers. 29 | 30 | #### Online 31 | 32 | - [__cppreference__](https://en.cppreference.com/w/) - A reliable online reference for C++ programming, providing detailed and up-to-date documentation on C++ language features, libraries, and standard functions. 33 | - [__LearnCpp__](https://www.learncpp.com/) - An online resource offering comprehensive tutorials and guides to help beginners learn C++ programming through step-by-step lessons and practical examples. 34 | - [__Stack Overflow__](https://stackoverflow.com/) - A popular online platform where programmers ask questions, share knowledge, and find solutions to programming-related issues from a community of developers worldwide. 35 | 36 | ### [Common terms](terms.md) 37 | 38 | ### [Frequently asked questions (FAQ)](faq.md) 39 | -------------------------------------------------------------------------------- /faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | - [Why should I not include cpp-files and instead use a header?](https://stackoverflow.com/questions/1686204/why-should-i-not-include-cpp-files-and-instead-use-a-header) 4 | - [What are the differences between a pointer variable and a reference variable?](https://stackoverflow.com/questions/57483/what-are-the-differences-between-a-pointer-variable-and-a-reference-variable) 5 | - [What does the explicit keyword mean?](https://stackoverflow.com/questions/121162/what-does-the-explicit-keyword-mean) 6 | - [Why is using namespace std considered bad practice?](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) 7 | - [What is the difference between #include \ and #include "filename"?](https://stackoverflow.com/questions/21593/what-is-the-difference-between-include-filename-and-include-filename) 8 | - [Why should C++ programmers minimize use of new](https://stackoverflow.com/questions/6500313/why-should-c-programmers-minimize-use-of-new) 9 | - [Why are #ifndef and #define used in C++ header files](https://stackoverflow.com/questions/1653958/why-are-ifndef-and-define-used-in-c-header-files) 10 | - [std::endl-vs-"\\n"](https://stackoverflow.com/questions/213907/stdendl-vs-n) 11 | - [Difference-between-static-and-shared-libraries?](https://stackoverflow.com/questions/2649334/difference-between-static-and-shared-libraries) 12 | - [What are the advantages of list-initialization using curly braces?](https://stackoverflow.com/questions/18222926/what-are-the-advantages-of-list-initialization-using-curly-braces) 13 | - [Is it a bad practice to use an if-statement without curly-braces?](https://stackoverflow.com/questions/2125066/is-it-a-bad-practice-to-use-an-if-statement-without-curly-braces) 14 | - [What is a lambda expression in C++11?](https://stackoverflow.com/questions/7627098/what-is-a-lambda-expression-in-c11) 15 | - [What are POD types in C++?](https://stackoverflow.com/questions/146452/what-are-pod-types-in-c) 16 | - [Can I call a constructor from another constructor (do constructor chaining) in C++](https://stackoverflow.com/questions/308276/can-i-call-a-constructor-from-another-constructor-do-constructor-chaining-in-c) 17 | - [When should static_cast, dynamic_cast, const_cast, and reinterpret_cast be used?](https://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used) 18 | -------------------------------------------------------------------------------- /getting_started.md: -------------------------------------------------------------------------------- 1 | # Getting started 2 | 3 | In order to get started with C++, you need a compiler. Simply put, a compiler is a software program that processes instructions written in a programming language 4 | and creates a binary file that the machine’s CPU can understand and execute. Compilers for Windows, Linux and MacOS are typically different. 5 | 6 | You are expected to use [CLion](https://www.jetbrains.com/clion/) in this course. CLion is a cross-platform IDE for C and C++, which is free for students. 7 | CLion comes bundled with CMake, so you do not have to install that independently. On Windows it also comes with a working compiler that uses the MinGW toolchain. 8 | However, you might consider using the MSVC compiler, which is tailored to Windows. 9 | 10 | ## Windows 11 | 12 | _Optional:_ Download [Visual Studio Community](https://visualstudio.microsoft.com/vs/community/). 13 | This is the preferred compiler on Windows, however, you can opt to use the MinGW toolchain that come bundled with CLion. 14 | 15 | ## MacOS 16 | Apple supports C++ with the Apple Clang compiler (included in Xcode). 17 | 18 | ## Linux 19 | In Debian-based distributions, the most well-known C and C++ compilers are gcc and g++. If your system doesn’t have the build-essential package 20 | installed in your system by default, you can install the latest available version from the default distribution repositories as follows: 21 | 22 | ``` 23 | sudo apt-get update && sudo apt-get install build-essential 24 | ``` 25 | 26 | 27 | ## Your first project 28 | 29 | With CLion and a working compiler setup: 30 | 31 | 1. Open CLion and choose `New Project`. 32 | 2. Under C++, choose C++ executable. 33 | 3. Specify the location of the project and set the language standard to C++17/20. 34 | 4. A CMake settings window should appear. If not goto `File->Settings->Build, Execution, Deployment->CMake`. The default settings are likely OK. 35 | - If you have installed Visual Studio under Windows, however, you might want to go `File->Settings->Build, Execution, Deployment->Toolchains` and add Visual Studio using the `+`. Make sure to select `x86_amd64` under `Architecture`. 36 | 37 | > Note. It's not a good idea to specify the location as a folder under cloud storage. This will result a large number of files beeing synchronized during building, 38 | > and of you use multiple PC's you'll end up with synchronization issues as the build files generated are PC specific. 39 | > 40 | > Furthermore, paths with special characters (including Norwegian ones) are likely to lead to hard to understand errors at least on Windows. Also _try_ to avoid paths with spaces. 41 | 42 | With the configuration done, CLion as now created a dummy "Hello world" project for you. It consists of two files: 43 | 44 | ##### CMakeLists.txt 45 | 46 | ```cmake 47 | cmake_minimum_required(VERSION 3.15) 48 | project(demo) 49 | 50 | set(CMAKE_CXX_STANDARD 17) 51 | 52 | add_executable(demo main.cpp) 53 | ``` 54 | 55 | ##### main.cpp 56 | 57 | ```cpp 58 | #include 59 | 60 | int main() { 61 | std::cout << "Hello, World!" << std::endl; 62 | return 0; 63 | } 64 | ``` 65 | 66 | C++ needs to be compiled prior to execution. 67 | 68 | > Click the green "hammer" in the upper right corner to build, or the green "play" button to build & run. 69 | > 70 | > You may also right click somewhere inside the `main` function to get a "run" context action. 71 | 72 | Executing the code should produce `Hello, World!` in the terminal window embedded in CLion (located in the lower panel). 73 | -------------------------------------------------------------------------------- /terms.md: -------------------------------------------------------------------------------- 1 | # Terms 2 | 3 | Whether you are new to programming in general or just C++, you will encounter a range of new terms. 4 | This page tries to put a meaning to as many such terms as possible. 5 | 6 | |Term| Explanation | 7 | |----|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 8 | |argument| Value passed to a function. | 9 | |assignment| Obliterates an object’s current value and replaces that value by a new one. | 10 | |block| Sequence of zero or more statements enclosed in curly braces. | 11 | |built-in type| Type, such as int, defined by the language. | 12 | |cast| An explicit conversion. | 13 | |class| Facility for defining our own data structures together with associated operations. The class is one of the most fundamental features in C++. Library types, such as `string` and `vector`, are classes. | 14 | |constructor| A special member function used to initialize objects. Each constructor should give each data member a well-defined initial value.| 15 | |constructor| A special member function used to initialize objects. Each constructor should give each data member a well-defined initial value.| 16 | |container initializer list| Specifies initial values of the data members of a class. The members are initialized to the values specified in the initializer list before the body of the constructor executes. Class members that are not initialized in the initializer list are default initialized. | 17 | |direct initialization| Form of initialization that does not include an `=`. | 18 | |encapsulation| Separation of implementation from interface; encapsulation hides the implementation details of a type. In C++, encapsulation is enforced by putting the implementation in the private part of a class. | 19 | |expression| The smallest unit of computation. An expression consists of one ormore operands and usually one or more operators. Expressions are evaluated to produce a result. For example, assuming `i` and `j` are ints, then `i + j` is an expression and yields the sum of the two int values. | 20 | |function| Named unit of computation. | 21 | |function body| Block that defines the actions performed by a function. | 22 | |function name| Name by which a function is known and can be called. | 23 | |friend| Mechanism by which a class grants access to its nonpublic members. Friends have the same access rights as members. Both classes and functions may be named as friends.| 24 | |header| Mechanism whereby the definitions of a class or other names are madeavailable to multiple programs. A program uses a header through a #include directive. | 25 | |initialize| Give an object a value at the same time that it is created. | 26 | |iterator| A type used to access and navigate among the elements of a container. | 27 | |main| Function called by the operating system to execute a C++ program. Eachprogram must have one and only one function named main. | 28 | |member function| Operation defined by a class. Member functions ordinarily are called to operate on a specific object. | 29 | |method| Synonym for member function. | 30 | |namespace| Mechanism for putting names defined by a library into a single place. Namespaces help avoid inadvertent name clashes. The names defined by the C++ library are in the namespace `std`. | 31 | |operator| Symbol that determines what action an expression performs. The language defines a set of operators and what those operators mean when applied to values of built-in type. The language also defines the precedence and associativity of each operator and specifies how many operands each operator takes. Operators may be overloaded and applied to values of class type. | 32 | |parameter list| Part of the definition of a function. Possibly empty list that specifies what arguments can be used to call the function. | 33 | |result| Value or object obtained by evaluating an expression. | 34 | |statement| A part of a program that specifies an action to take place when the program is executed. An expression followed by a semicolon is a statement; other kinds of statements include blocks and `if`, `for`, and `while` statements, all of which contain other statements within themselves. | 35 | |std| Name of the namespace used by the standard library. `std::cout` indicates that we’re using the name cout defined in the std namespace. | 36 | |undefined behaviour| The result of executing a program whose behavior is prescribed to be unpredictable, in the language specification to which the computer code adheres. | 37 | |uninitialized variable| Variable that is not given an initial value. Variables of class type for which no initial value is specified are initialized as specified by the class definition. Variables of built-in type defined inside a function are uninitialized unless explicitly initialized. It is an error to try to use the value of an uninitialized variable. Uninitialized variables are a rich source of bugs. | 38 | |variable| A named object. | 39 | |vector| Standard library type that holds a collection of elements of a specified type. | 40 | --------------------------------------------------------------------------------