# 45C Full Review
# Main Overview of Concepts
The "main ideas" of this class revolve around these topics:
- Syntatic differences of C++ to other languages and programming in C++, File Structure, Headers, etc.
- The "quirks" of C++ -- Global Declarations compared to Local Ones, Lifecycle of Objects, etc.
- Pointers, References, Dynamic Memory Allocation, Contiguous Memory Allocation and Pointer Arithmetic, Pass-by-value/reference, Memory Manipulation, *Strings*
- Compiler Optimization (through Constant Expressions and Parameters, Copy/Move Construction, etc.)
- Classes and Objects, Definitions of Classes, Inheritance and Polymorphism, Abstract Base Classes and Abstraction
- ICS46 Prep -- LinkedLists, Dynamic Allocation, `new & delete`, Working with Memory and Data Structures, Defining Data Structures
- `operator` overloads
- High-Level C++ -- Lambdas, Templates, Namespaces (and Cumulativity of them), Exceptions and Error Handling
- The Standard Library (STL) -- Iterators, Algorithms, and Standard Containers (`vector`, etc)
- Other important notes documents: [Iterators and Algorithms to Know](/SQmu4fK6SR-E5xnWaHVPzQ), [Data Structures to Know (C++)](/xXs4jZSRRUClGEEEtC5Q5A), [45C Reading Quiz Conceptual Review](/XNSgwrf9QNqn6AlxikCxVA)
# Syntactic Differences and Intrinsic Features
## Initialization, Construction, etc.
Every object in C++ has a lifecycle. It:
- Is Born
1. Allocated
2. Constructed `C()`
- Lives
- Dies
1. Destructed `~C()`
2. Deallocated
```cpp=
// FOR PRIMITIVES
int x; // declaration, memory allocated, indeterminate value
x = 5; // rvalue copy assignment
int x { 5 }; // declaration, memory allocated, initialized directly
int x(5) // ^ same here ^
int x = 5 // initialization by rvalue copy
```
- There are 6 main types of constructors in C++, and one destructor
1. Direct Constructor `C()`
2. List Constructor (std::initializer_list) `C{}`
3. Copy Constructor `C(D())`
4. Copy Assignment `C() = D()`
5. Move Constructor `C(std::move(D()))`
6. Move Assignment `C() = std::move(D())`
7. Destructor `~C()`
## Operator Precedence Chart
- [via cppreference -->](https://en.cppreference.com/w/cpp/language/operator_precedence)

## LValues and RValues
Lvalues, known as locator values, are objects that represent an *identifiable* location in memory (i.e. we can get a pointer to them)
- Usually, these are on the LHS of an assignment
RValues are temporary values that do not contain this persistence of memory
- Usually, appears on the RHS of an assignment, and cannot use the & operator
```cpp=
int x = 5; // 5 is an rvalue, x is now an lvalue
&5 // --> error
&x // --> memory addr of x
```
#### Aside: RValue References
RValue References allow you to bind memory temporarily to rvalues, and are declared with `&&` syntax. They essentially give a temporary object that is about to be destroyed a temporarily accessible location in memory, such that we can access values with it.
- We can now access them in a deeper scope, and perform different operations on them, such as move assignment and construction for ease of use.
```cpp=
#include <iostream>
class Resource {
public:
Resource() { std::cout << "Resource acquired" << std::endl; }
~Resource() { std::cout << "Resource destroyed" << std::endl; }
};
void useResource(Resource&& res) {
std::cout << "Using resource" << std::endl;
}
int main() {
useResource(Resource()); // Temporary Resource object bound to rvalue reference
std::cout << "End of main" << std::endl;
// Resource object is destroyed here
return 0;
}
```
- Prime example of how `res` can be used even when its destroyed right after the `useResource(Resource())` call
## Templates
Templates are a way to write more dynamic code, with functions that conform to different types.
```cpp=
// here's an example of a print function that prints a type that
// has a std::ostream& operator<< defined
template
<typename T=[default]> // defaults are optional
void print(const T& arg) {
std::cout << arg << std::endl;
}
// we can now use print with `int`, `double`, `float`, anything!
```
- Entire classes can have templates, functions can have templates, and even members can have templates!
## Lambdas
Lambdas are anonymous functions that allow for single-use or utility-use or function-object usage.
- More detailed function-object usage for all types of functions is provided through the `<functional>` header.
```cpp=
// declaring a lambda:
using return_type = ???;
using param_type = ???;
return_type lambda_name = [](param_type p) {
return some_function(p);
};
// for example:
auto is_larger_than_5 = [](const int& x) {
return x > 5;
};
```
- In the brackets, you define a *capture* type.
- `[&var]` -> capture variable by reference
- `[var]` -> capture variable by copy (value)
- `[]` -> capture nothing
- `[&]` -> capture all by reference
- `[=]` -> capture all by copy (value)
## Namespaces

## Inheritance and Polymorphism, and ABCs
### Inheritance
NOTE: The first parameter in all member functions in C++ is always implicitly `this`, and this bound by the compiler!
In C++, Inheritance works as follows:
```cpp=
class A {};
class B : public/protected/private A {};
// now, B is-a A
```
There are 3 types of inheritance:
1. Public Inheritance: `public` and `protected` members of the base class are `public` and `protected` in the derived class respectively.
2. Protected Inheritance: `public` and `protected` members of the base are strictly `protected` in the derived
3. Private Inheritance: `public` and `protected` members of the base are strictly `private` in the derived
- Since C++ supports virtual inheritance, there's the slight issue that arises for MROs similar to ICS 33 Python examples. If you do `[qualifier] virtual Base`, then it makes sure that these chains are avoided and multiple inheritance doesn't cause these overlaps.
- You can do multiple inheritance with commas easily
There are 3 ways to qualify members in Classes:
1. `public`: Visible to all
2. `protected`: Visible to `this` and `this` type, and all derived
3. `private`: Visible only to `this` and other instances of `this` type
You can only access protected members of the base *directly* in the class or in friend functions
### Polymorphism and ABCs
To make a base class *abstract*, you can tag a class's member with `virtual`. This enables **dynamic binding** of the class and its derived clases.
- `virtual` functions can have default implementations, unless they are tagged with `= 0;`, then they are *purely virtual* and the derived class *must* define them
- If the function isn't tagged with virtual, then the derived class's functions will be passed up in favor of the base class.
- Destructors can be virtual to make sure all base class resources get cleaned effectively, and also make sure that derived classes that `are-a` base class can still clean their memory up effectively.
- Constructors cannot be virtual, as the type of obj. being constructed must be known at compile-time, and the binding is at run-time (hence, *dynamic*)
The order of construction and destruction in (non/multiple) inheritance in C++:
**Construction**
1. Base Constructor
2. First Derived Constructor
3. Nth Derived Constructor
**Destruction**
1. Nth Derived Destructor
2. First Derived Destructor
3. Base Destructor
*tide-in, tide-out relationship
- For a more detailed look at polymorphism, think of shapes and areas!
## Exception Handling
```cpp=
try {
// Code that may throw an exception
}
catch (const std::exception& e) {
// Handle exceptions derived from std::exception
std::cerr << "Exception: " << e.what() << std::endl;
}
catch (...) {
// Handle all other exceptions
std::cerr << "Unknown exception caught" << std::endl;
}
// To raise an error:
throw int(5); // or
throw std::string("Hello");
// etc..
```
# Pointers
```cpp=
int x(5);
int* ptr = &x; // stores a pointer to x
int& ref = x; // stores a reference to x
// NOTE: the "&" means different things on different sides
```
Editing a reference is like treating it directly like the object. It will update the same memory, but act like a normal int. Meanwhile, ptr is the memory ITSELF, so editing that is actually changing memory.
- Doing *ptr *dereferences* the pointer, returning 5 in this case, by *providing* the value directly.
```cpp=
int x(5);
int* ptr = &x;
*ptr = 6;
cout << x << endl;
// this will output 6
int x(5);
int& ref = x;
ref = 6;
cout << x << endl;
// this will also output 6
```
## Dynamic Memory
```cpp=
int* x = new int(5);
// this dynamically allocated x on the HEAP memory area
```
Note that there are **3** memory areas in C++:
1. **Global Static**
- Allocated before main() and deallocated after main()
- This includes all variables tagged with `static` anywhere in the program, not functions though.
2. **Stack**
- Born and Dies within a scope, and predefines memory allocated to the program, defined at compile time and populated randomly at run time.
- The stack is *fast*, because the OS knows what to do with that stuff in the program already
3. **Heap**
- Allocated with `new` or `new[]` and deleted with `delete` or `delete[]`. Heap memory is *always* alive from `new` to `delete`, and if you forget to delete it, it's a memory leak!
- This allows for run-time memory allocation, but it is at the cost of performance.
```cpp=
// Dynamic Allocation Example
#include <iostream>
using namespace std;
class Student {
std::string name;
int year;
std::string* classes;
public:
Student(std::string n, int y, std::string* c) : name(n), year(y), classes(c) {}
void print(ostream& out) {
out << name << ", Year: " << year << endl;
for(int i = 0; i < 2; ++i) {
// dynamic arrays don't store sizes, i'm just hardcoding for ex.
out << this->classes[i] << endl;
}
}
~Student() {
delete[] this->classes; // manually clean up memory
cout << "Here!";
}
};
int main() {
std::string* classes = new std::string[2]{
"ICS 45c",
"ICS 6D"
};
std::string* classes2 = new std::string[2]{
"ICS 45c",
"ICS 6D"
};
Student* s = new Student("Test", 1, classes);
s->print(cout); // now we direct through a ptr
delete s; // no "Here!" output without this!
Student s2("Test", 2, classes2);
s.print(cout) // this is direct, no ptr
delete[] classes;
delete[] classes2; // don't forget to clear up dynamic memory!
}
// THIS WILL OUTPUT THE FOLLOWING:
Test, Year: 1
ICS 45c
ICS 6D
Here! // heap delete
Test, Year 2
ICS 45c
ICS 6D
Here! // stack dealloc
// Note how the destructor for the stack-allocated "s2"
// was called automatically, but we had to do this manually for
// heap allocation
```
## Smart Pointers
Since C++ has so many issues with memory leaks, because humans are inherently fallible and will forget to call `delete[]`, there are a couple utilities added in to make life easier
Defined in `#include <memory>`, `std::shared_ptr<T>` and `std::unique_ptr<T>`
#### `std::unique_ptr`
Unique pointers *entirely own* the object that they point to, and delete that object as soon as the unique pointer goes out of scope. It also ensures that it is the *only* pointer to that resource.
- Ownership must be transferred by std::move in the memory header.
```cpp=
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> ptr1 = std::make_unique<int>(10);
std::cout << *ptr1 << std::endl; // Output: 10
std::unique_ptr<int> ptr2 = std::move(ptr1); // Transfer ownership to ptr2
if (ptr1 == nullptr) {
std::cout << "ptr1 is now nullptr" << std::endl;
}
// ptr1 goes out of scope, but it does not own the resource anymore
// ptr2 goes out of scope, and the resource is automatically deleted
}
```
#### `std::shared_ptr`
Shared pointers manage shared ownership of a resource. That way, multiple instances of a shared_ptr can point to the same resource, which is destroyed once the last shared ptr goes out of scope.
- Reference Counting
- If needing to make another one, copy a shared ptr, don't `make_shared` to A NEW ONE
```cpp=
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> ptr1 = std::make_shared<int>(20);
{
// std::shared_ptr<int> ptr2 = std::make_shared<int>(20); DO NOT DO THIS!!!!!!!!!
std::shared_ptr<int> ptr2 = ptr1; // Shared ownership
std::cout << "ptr2 use_count: " << ptr2.use_count() << std::endl; // Output: 2
} // ptr2 goes out of scope, but resource is not deleted
std::cout << "ptr1 use_count: " << ptr1.use_count() << std::endl; // Output: 1
// ptr1 goes out of scope, resource is deleted
}
```
- When using these special constructors for both of these, you can actually just pass in the initialization parameters directly, rather than making a shared ptr to an obj declaration.
### Other Random Things to Note:
#### FileIO in C++
```cpp=
ifstream in{ "file.txt" }; // read-in file
ofstream out{ "output.txt" }; // write-out file
```
C-Strings are defined as `const char*`;
#### Memory Management
- C++ default is *memberwise* copy/move construction/assignment. If you don't define these behaviors, it just does a shallow copy of everything for you.
- If you need to manage memory resources, you need to define your own copy/move construction/assignment to make sure that your resources aren't pointing at the same thing and are managed properly.
- Default is *memberwise*, preferred is *deep*