Welcome to the beginning of my CS notes! This course focuses on the fundamentals of object-oriented programming principles using the C++ language. The emphasis later will be more on programming patterns that syntactic peculiarities, but first we start with the design of the language.
As with my other notes, keyword search the page for easier navigation, I've created a 'topic note' in the form of a cpp script for each big topic covered in the course with comments explaining the syntax.
//Function Prototypes
//Caleb Magill
//2/12/25
//NOTES
/*
Purpose;
To allow the functions to be used in the scope of the main() loop if stored outside it
*/
//EXAMPLES
/**/
//Example with function that doesnt accepct any arguments nor return any values
void happyBirthday(); //Function prototype
int main()
{
happyBirthday();
return 0;
}
void happyBirthday(){
cout << "Happy Birthday to you!" << endl;
}
//Example with function that accepts arguments and returns a value
int add(int x, int y); //Function prototype
//or
int addAlso(int, int); //Function prototype
//^Don't need to name the arguments in the prototype
int main()
{
int sum = add(5, 7);
cout << "The sum is: " << sum << endl;
return 0;
}
int add(int x, int y){
return x + y;
//^Will return x+y and store it in the variable that invoked the function
}
//SUMMARY
/*
- Function prototypes allow you to use function inside or outside main() loop
- Put function prototypes before main() loop, invoke function in main() loop, and define function after main() loop
*/
//Function Types Notes
//Caleb Magill
//2/12/25
//NOTES
/*
Void - no data returned
Bool - Function returns bool state (either true or false)
Int - Function returns an integer
Double - Function returns a double
Char - Function returns a character
*/
//EXAMPLES
/**/
//Example of a bool function;
bool isEven(int); //Function prototype
int main(){
int x = 5;
x = isEven(x); //Function call
if(isEven(x) == 1){ //< func value will be 1 if func it true
}
else{ //< func value will be 0 if func it false
return 0;
}
}
bool isEven(int num){
if(num % 2 == 0){
return true;
}else{
return false;
}
}
//^Simply returns true or false for the bool
//SUMMARY
/*
Bool Functions
- Return true or false
- Return type is bool
- Function body must return a bool value
- Function value 1 means true, 0 means false
Void Functions
- No data returned
- Return type is void
- Function body does not return a value
- Can still do stuff within scope of func like print to console
Other Types
- Return type is function type
- Function body must return a value of the correct type
*/
//Caleb Magill
//2/12/25
//Understanding Implicit & Explicit Type Conversion
//NOTES
/*
Recall:
- int truncates decimal, not round
- double adds decimal places (0s if from int)
Type conversion to be used in situations where need to get decimal back but using int variables
*/
//EXAMPLES
/** */
//Implicit
int x = 3.14 //Output would be x=3 because it truncates to become int
//Explicit
double x = (int) 3.14;
//^ x = 3, stores as a double but first truncates to int then stores as doulbe
int correct = 8;
int questions = 10;
double score = correct/questions * 100;
//^score will actually be 0 as int division still truncates even though storing in double var
//Instead by using this...
double score = correct/(double)questions * 100;
//^This will return 80 as it is now instead explicitly casting questions as a double var
//SUMMARY
/*
Implicit - auto-conversion
Ex. int x = 3.14
Explicit - explicitly expressing
Ex. double x = (int) 3.14;
*/
//Pass-By-Value vs. Pass-By-Reference
//Ca13b M4gi11
//2/12/25
//NOTES
/*
Pass-By-Value is one most commonly used;
You are making a copy of that variable's value and acting upon that,
but the original variable holding the value is not changed.
Pass-By-Reference is acting upon the variable itself,
not just its stored value
*/
//EXAMPLES
/**/
//Pass-By-Value:
void passByValue(int y){
y = 99;
}
int main(){
int x = 5;
passByValue(x);
cout << x; //5
}
//Pass-By-Reference:
void passByReference(int &x){
// ^the '&' symbol is what makes this pass-by-reference
x = 99;
}
int main(){
int x = 5;
passByReference(x);
cout << x; //99
}
//Conversely,
int passByValue(int y){
y = 99;
return y;
}
int main(){
int x = 5;
x = passByValue(x);
cout << x; //99
//^This is still pass-by-value but you are setting the new value to a variable
}
//SUMMARY
/*
Pass-By-Value is one most commonly used;
You are making a copy of that variable's value and acting upon that,
but the original variable holding the value is not changed.
Pass-By-Reference is acting upon the variable itself,
not just its stored value
*/
//Prefix vs. Postfix & Compound Assignment
//Caleb Magill
//3/4/25
//NOTES
/*
Prefix - adds to the variable before any additional operation is performed on it
(++variable)
Posfix - adds to the variable only after every other subsequent operation is performed on it
(variable++)
Compound Assignment - combines arithmetic operation with assignment operation
(+=, -=, *=, /=, %=)
*/
//EXAMPLES
int x = 5;
int y = 5;
std::cout << "x++: " << x++ << endl;
//^Output: x++: 5
std::cout << "++y: " << ++y << endl;
//^Output: ++y: 6
//Compound Assignment
int z = 5;
z += 5;
//^Equals 10 since it is shorthand for z + 5 = z, z = 5 to start
//SUMMARY
/*
Use prefix if you desire to increment variable before subsequent operations,
otherwise use postfix if you want to increment only after operaitons are complete.
Compound assignments are just shorthand ways of expressing an arithmetic operation
just used for cleaner code
*/
//Arrays
//Caleb Magill
//2/19/25
//NOTES
/*Array - Collection of Values(can also be string)
- Stores values that can be accessed by an index number
You cannot add additional indexes and values post-declaration, though
You can change the value stored at an index post-declaration however
Arrays can only contain data values of the same type, so cannot store an int at one index and a string at another
Don't have to declare values within array initially, however you must set the size of the array upon declaration
Referred to as aggregate construct, as it allows manipulation of multiple entities as single entity
*/
//EXAMPLES
#include
std::string car[] = {"corvette", "lamborghini", "ferari"};
//^basic example of an array, can store as many elements as you define it to have
//index count starts at 0, so the index 0 of array "car[]" is "corvette"
//Note; an array's size remains fixed once it is declared, which means you cannot give it more values to store within it
car[0] = "Honda Civic";
//^you can modify the value being stored within an index at any time
opps[5];
//^can declare array without values, however must specify size of array
//^This means 5 values & 5 indexes contained within array
opps[0] = "bloodhound";
opps[1] = "o-block";
//etc...
int pancakes[2];
//^can (but don't have to) specify type when declaring array, won't specify type until values seeded within indexes
//SUMMARY
/*
- All data contained within an array must be of same type
- An index points to the value stored within an array
- Must specify size of array when declaring it
- Out-of-bounds errors occur when try and access an index that doesn't exist
- Computers start counting at 0
*/
//Strings
//Caleb Magill
//2/19/25
//NOTES
/*
Strings
- construct designed for processing sequence of characters
Must include "#include " to use
&
"using std::string"
When getting a string input using cin, strings use extraction operator,
meaning it strips white space and assigns the first token (word) to the string.
SEE EXAMPLE 2
getLine() function allows the user to input sentence of string,
SEE EXAMPLE 3
Concatenation = combining values into string value
SEE EXAMPLE 4
Can compare strings as values;
- strings are compared lexicographically,
- letters in alphabet are in increeasing order
- longer word is greater than short word
SEE EXAMPLE 5
Multiple predefined functions are made for strings,
size() - current string size (number of characters in string)
length() - same as size()
max_size() - defines the max size of characters allowed in string
empty() - returns true if string is empty
SEE EXAMPLE 6
Can access an element (character) within a string
SEE EXAMPLE 7
Can also return a substring of a string,
where substring is a part or piece of an original string
SEE EXAMPLE 8
Can instert characters or substrings into any position within already-defined string
SEE EXAMPLE 9
Other such functions such as append() and erase() which both operate w/ same syntax as previous functions
*/
//EXAMPLES
#include
using std::string;
/**/
string myString = "Hello World";
string str1 = 'k';
//^Example of valid string declaration
//^Must use "" to contain string
//EXAMPLE 2
string s;
cin >> s;
//"hello world"
//^Example user input
//This input would only assign "hello" to the value of string 's'
//EXAMPLE 3
string s;
getLine(cin, s);
//"hello world"
//s = "hello world"
//EXAMPLE 4
string s1 = "hello";
string s2 = "world";
string s3 = s1 + " there " + s2;
//s3 = "hello there world"
//EXAMPLE 5
string s1 = "accecpt", s2 = "access", s3 = "accecptance";
//^ s1 > s2 < s3
//Longer strings are greater in value
//numbers < Uppercase Letters < lowecase letters
//EXAMPLE 6
string s= "Hello";
cout << s.size();
//^Outputs "5"
//As this is the number of characters contained within the string
//EXAMPLE 7
string s = "hello";
cout << s[6];
//^Would be an error as there is no 6th character within string s
//EXAMPLE 8
string s = "hello";
cout << s.substr(2, 3);
//^Output: "llo"
//2 denotes index of first character, and 3 denotes length (how many characters to print from there)
//Remember count starts at 0
//EXAMPLE 9
string s = "place";
cout << s.insert(1, "a");
//Output: "palace"
//1 denotes starting index to insert substring before (so before the l since it is index 1, then the subsequent substring)
//SUMMARY
/*
- Must include;
- #include
- using std::string
- Strings are not built-in type, but they store sequence of characters
- Many built-in functions that allow for the manipulation of the string value
- Function similarly to arrays but for characters, can access elements and substrings within string
*/
//vectors.cpp
//Caleb Magill
//4/17/25
//---------------------------------------------------
//NOTES
/*
Vectors
- More powerful versions of arrays
- Vectors are a class
- Vectors require own include statements (See Example1)
- Not a built-in type
- Require "#include "
- Additionally require "using std::vector"
- Vectors have many built-in functions (See Example2)
- empty()
- Checks whether the vector is ENTIRELY empty, doesn't consider individual indicies,
if any of the contained indicies hold a value it is not empty
- clear()
- Removes all the values AND the indicies themselves, meaning the new size of the,
vector is zero
- size()
- Returns the number of indicies within the vector, regardless of if they all,
hold a value within them
- resize(newSize)
- Will resize the number of indicies within the vector to the argument number,
if making index smaller, any existing elements at end that need to be removed,
will be removed until the size matches that of the argument, doesn't shift any values
- push_back(number)
- Will add the argument number as a new index at the end of the existing vector,
meaning an additional index will always be created and it will be assigned the value,
of the argument
- pop_back()
- Will remove the last index of the vector, whether it holds a value or not, doesn't,
take an argument and always removes the last index of the vector
- front() & back()
- front() always is going to access the first index in the vector, the functionality is,
simly to access it, from there can mofify it, like "vector.front() = 80;"
- back() always accesses the last index of the vector, same functionality as front(),
where you can then modify it
- Vectors and Functions (See Example3)
- Vectors can be used with functions
- Function can accecpt arguments of vectors
- Functions can return vectors
- Vector Iterators (See Example4)
- Functions similarly to a pointer
- Iterators iterate within all the indicies of the vector
- After iterator is declared, can set it equal to vector.begin() to begin iterating,
over the vector
- Iterator Arithmetic
- Incrementing and decrementing the value of the iterator dictates which index it,
is currently on
- Can use the dereference operator to access the element the iterator is currently on
- Can also implicitly invoke for a situation like an argument of a function
- Iterator Invalidation & Pointers(See Example5)
- Iterator invalidation means that, given a modification to a vector which has a pointer,
or iterator on it, the modification will invalidate the values of the pointer and iterator,
since the memory address of the indicies is modified upon modification to the size of a vector
- As a general rule: always reassign pointers & iterators post-size modification, since the,
addresses would be changed
- Pointers & Vectors (See Example6)
- Pointers are declared normally for use with vectors
- Can also dynamically create vectors in heap with memory allocations, also typical declaration
- Iterator Algorithms (See Example7)
- All you gotta know for this is that vectors use algorithms for stuff like sorting and,
locating specific elements within itself
- Need to include "#include " to work
- Predefined functions associated with this, can also use the implcit invocation of an,
iterator as argument of such function, most algorithms take iterator as argument
- Typedef (See Example8)
- Super useful stuff
- Basically allows you to redefine a type's name and can use that to declare objects of that,
type
- Think about it like a shorthand way of changing the spelling of a type
*/
//---------------------------------------------------
//EXAMPLES
/**/
//--------
//Example1 - Declaring a vector + file setup
#include
using std::vector;
int main(){
vector v;
//^Name vector followed by type followed by name
//Uninitialized size
vectorvec(10);
//^Initialized with a size of 10
vec[1] = 2;
//^2nd index will hold value 2, just as normal array
}
//--------
//Example2 - Demonstrating built-in functions of vectors
int main(){
vectorv(5);
//empty()
while(v.empty()){
//run functionality coniditional on vector being entirely empty
}
//resize()
v.resize(6);
//^Reassigning vector to have 6 indicies rather than 5
//front()
v.front() = 5;
//Setting index 0 to value of 5
//back()
v.back() = 10;
//Setting index 4(5) to value of 10
//size()
if(v.size() < 5){
//run functionality conditional on vector having less then 5 indicies
}
//push_back()
v.push_back(10);
//^Adding an index to the end of the vector and assigning it the value of 10
//pop_back()
v.pop_back();
//^Removes the last index of the vector, in this case from above holding value 10
//Remember: removes index itself, so now vector 'v' has one less index
//clear()
v.clear();
//^Vector is now entirely empty and has no indicies
}
//--------
//Example3 - Utilizing Vectors with Functions
int main(){
vectorv(10), vector1(5), vector2(3);
v.funcReturnVect(vector1, vector2);
}
void funcArgVect(vector v, vector vec&){
//^Pass-by-value
//^Pass-by-reference
//do something with the argument vectors
}
vector funcReturnVect(vectorvector){
//^Can specify which type of vector to return
//Remember: return type of vector must match definition type
vectorv(1);
return v;
//^Returns a vector 'v'
}
//--------
//Example4 - Vector Iterators
int main(){
vectorcharV(5);
vectorcharV2(5);
vector::iterator iv;
//^Must use accessor followed by 'iterator'
//^Notice: iterator type must match that of the vector you would like it to iterate over
iv = charV.begin();
//^Beginning the iteration on a vector, iterator will always begin on first index
int newNum = *iv;
//^Assigns the int 'newNum' the value the iterator is currently pointing at
++iv;
//^Increments the iterator along the vector it is currently pointing at
iv = charV2.begin();
//^Now begins iterating along a different vector, no need to do any dereferencing,
//operation in order to change the vector it is iterating along
//^Once this occurs, 'iv' is reset to position 0 of 'charV2', the value within iv,
//in respect to the previous vector is completely lost, meaning the position value,
//doesn't remain upon the switching of vectors
charV.erase(charV.begin() + 2);
//^Invoking implicitly iterator of vector used as argument of function
}
//--------
//Example5 - Iterator Invalidation
int main(){
vectorv(10);
vector::iterator itr = v.begin();
v.erase(itr);
//^Iterator becomes invalid since the index it was on, pointing at since similar to pointers,
//was deleted, meaning it must be redefined so it can once again become valid
int* potr = &v[1];
//^Assigns the memory address of the 2nd index of the resized vector to pointer ptr
}
//--------
//Example6 - Vectors & Pointers
int main(){
vector* ptr;
//^Creating pointer of correct type
ptr = new vector(5);
//^Creates new vector of type 'int' in heap, has five indicies
(*ptr)[2] = 5;
//^Assigning the third index the value of five
vectorv(10);
int* potr = &v[1];
//^Assigns the memory address of the 2nd index of the resized vector to pointer ptr
}
//--------
//Example7 - Algorithms with Vectors
#include
//^Remember to utilize include statement
int main(){
vectorv(5);
sort(v.begin(), v.end());
//^Calls predefined sort function, while implicitely invoking pointer to use with,
//iteration of the vector
vector::iterator found = find(v.begin(), v.end(), 55);
//^Will return the index of where the argument value was found, up to discretion what,
//occurs next given the vector wouldn't be modoifeid any further
}
//--------
//Example7 - Typedef
int main(){
typedef vector vect;
typedef vector::iterator iter;
vect a, b, c;
iter d, e, f;
//^Simply just saving presets for a certain name & using shorthand form to keep it that way
}
/**/
//---------------------------------------------------
//SUMMARY
/*
- More dynamic implementation of arrays as both are aggregate constructs, whose,
names function as pointers to the construct
- Need special #include & using std::vector statements included
- Have many useful predefined functions to modify and dynamically resize
*/
//multiArrays&vectorsOfVectors.cpp
//4/24/25
//Caleb Magill
//----------------------------------------------------------------------------------------------------
//NOTES
/*
Multi-Dimensional Arrays
- Used to create arrays with mutliple axies, axises???
- Number of Dimensions determined on declaration (See Example1)
- First argument is number of rows, typical 1d space
- Second argument is number of columns
- Every subsequent argument is additional axis
- Can simply use coordinates of index to access it (See Example2)
- Index out-of-bounds error still exists when index trying to access doesnt,
exist
- Iteration with Multi-Arrays (See Example3)
- Simply use nested for() loops to iterate over a multi-array
- Good practice would be to iterate in the order of axis, where arr[a][b][c],
a as parent for() and c is innermost for() loop
- Can be used normally within functions, arguments of function, objects, etc.
Vectors of Vectors
- An alternative to multidimensional arrays
- Better practice since you can dynamically modify size of vector
- Declaration of vector of vector is to just pass vector type as a vector of a,
type, (See Example4)
- Can add multidimensions with this concept as well, nesting multiple times
- Can define children vector sizes on declaration, however, could also intialize,
visually as a cube, then remove indicies on each axis
- Accessing indicies is the same as multidimensional arrays
- Same Rules of vectors applies to vectors of vectors
- Can modify and utilized functions same as vectors not of vectors
- Difference between multiarrays, besides modular abilities, additionally, can resize,
children independently of parent, jagged array (See Example 5)
- Think of it as one complete vector of type vector, whereas each individual vector,
enclosed within the initial vector is an additional axis, same as multidimension arrays
- Nested vectors of parent are uninitialized unless otherwise specified at,
declaration (See Example 4)
- Accessing elements of nested vectors looks similar to that of multiarrays
- Inner vectors don't get own name as they are apart of the greater whole
*/
//----------------------------------------------------------------------------------------------------
//EXAMPLES
/**/
//-----------
//Example1 - Declaration of Multi-Array
int main(){
int a[3][4];
//^Rows
//^Columns
//Visualization of above Declaration
/*
C0 C1 C2 C3
R0 * | * | * | * |
R1 * | * | * | * |
R2 * | * | * | * |
*/
}
//-----------
//Example2 - Accessing Indicies of Multi-Arrays
int main(){
int a[3][4];
a[1][2] = 21;
//^Simply using coordinates of index to access & assign it value
}
//-----------
//Example3 - Iteration over Multi-Array
int main(){
int a[5][6];
for(int i = 0; i < 5; i++){ //for() loop to iterate rows
for(int j = 0; j < 6; j++){ //for() loop to iterate columns
//code to iterate through
}
}
/*
Visualization of above iteration
1
----------------> | 2
* * * * * v
3
----------------> | 4
* * * * * v
* * * * *
* * * * *
* * * * *
* * * * *
*/
}
//-----------
//Example4 - Declaration of Vector of a Vector
#include
using std::vector;
int main(){
vector> myVector(5, vector(6));
//^Parent vector type is child vector of type int
//^Parent vector has five indicies,
//child vector has six indicies
vector>> myNewerVector(5);
//^Continuous seeding creates new axies just as multidimensional arrays
//^Important Note: this statement only initializes parent vector,
//meaning all subsequent children are uninitialized
myNewerVector[2][2] = 21;
//^This is an index out of bounds error since the child vector is uninitialized
}
//-----------
//Example5 - Jagged Vectors
int main(){
vector>vecOfVec(5, vector(5));
//Visualize a square with five rows and five columns
for(int i = 0; i < 5; i++){
vecOfVec[i].pop_back(i);
//^Removing the last element within each row, progressively adding with the value,
//of 'i' meaning jagged pattern occurs since removing and additional index each time
}
//Original
// 0 0 0 0 0
// 0 0 0 0 0
// 0 0 0 0 0
// 0 0 0 0 0
// 0 0 0 0 0
//Modified (Jagged)
// 0 0 0 0
// 0 0 0
// 0 0
// 0
//
}
/**/
//----------------------------------------------------------------------------------------------------
//SUMMARY
/*
- THink of Both multiarray & vector in vector as one aggregate construct with multiple,
axies
- Vectors of vectors are very similar to multiarray however more dynamic, as multiarray,
size is fixed upon declaration
*/
//dynamicMemoryAllocation.cpp
//Caleb Magill
//4/9/25
//---------------------------------------------------
//NOTES
/*
Pointers Revisited
- Recall all previous rules about pointers
- Pointers are foundation for dynamic memory allocation stuff
Dynamic Memory Allocation
- Overview
- Two memory assignment operations
- Allocation = assignment (claim) of memory for a particular variable
- 'new' operation used to assign memory
- Deallocation = releasing (freeing) of memory
- 'delete' operation used to remove memory
- Two memory locations
- Stack = preallocated memory we have no control over
- Heap = memory we are able to control and have available to ourselves
- Memory Assignment (See Example1)
- 'new' = allocates memory in heap to variable
- 'delete' = removes allocated memory portion in heap
- IMPORTANT: '*ptr', with dereference operator in front, in the context of dynamic memory allocation, functions like,
a way of referring to the content of the memroy location pointed at by 'ptr'
-`Memory ALlocation Types
- Just important to keep in mind
- 3 types
- 1 = static variable/constants, allocated in special place for static variables
- Allocated when program begins executuin, deallocated after program ends
- 2 = automatic variables, local variables, normal scope applies, allocated apart of program stack,
allocated when function is invoked, deallocated when function ends, think scalar variables
- 3 = dynamic variables; programmer-controlled, the new stuff, stored in heap not stack,
alocated and deallocated when we choose to
- Problems w/ Memory Leak (See Example2)
- A pointer that points to a dynamic variable is the only way to access that variable
- If the pointer is reassigned without first deleting that dynamic variable, memory leak occurs
- Consider, for example in the context of a for() loop, how big of an issue this could be given that a bunch,
of variables are created within heap and subsequently lost
- Pointers & Arrays (See Example3)
- An array name functions as a pointer in of itself since it holds indicies with values within, however,
the name itself isn't a single storage-space for a value, since it is an aggregate
- Utilizing pointer arithmetic, recall from notes on pointers, you can access individual indicies of array,
and assign/modify values of
- Some exceptions occur in the rules of syntax as it pertains to the typical process of dereferencing and referincing,
these exceptions occur due to the fact that an array name function as a pointer, not just in the visualization sense for,
our own reason, but additionally, with how it interacts with the compiler
- Arrays can be created in the heap through pointers, once they're created, and assigned a pointer, the indicies of the,
the array can be accessed like regular arrays
- Pointers & Functions (See Example4)
- Pointers can be used entirely within the scope of functions, similarly, memory allocations within the heap,
can also be created within the scope of functions
- IMPORTANT: once a memory allocation is created within the heap, it exists indefinately until it is deleted,
meaning it still exists outside the scope of the function it was created within
- If creating a new memory allocation in the heap within the scope of a function, ALWAYS DELETE BEFORE FUNCTION EXIT
- Can also have functions that return pointers
- Can also do pointers to pointers however this isn't super important to recall
- Dynamic Objects (See Example5)
- Recall relationship between pointers and objects from pointers notes
*/
//---------------------------------------------------
//EXAMPLES
/**/
//Example1 - Demonstrating Two Memory Assignment Operations; 'new' and 'delete'
int main(){
int *ptr;
ptr = new int;
//^Allocating a space in the heap big enough to hold an int, the 'ptr' pointer is pointing at that location in heap
*ptr = 5;
//^Need dereference operator in front, basically functions to say go to the content of the memory address and act,
//upon that, meaning assign value
//Assignes value 5 in int created in heap, ptr is pointing to it in heap
ptr = 5;
//ERROR
//Because this above line is sayying you are trying to assign the value of 5 to the pointer itself, rather than acting,
//on the int it is pointing toward
delete ptr;
//^This line is removing the int created within the heap, deallocating its memory, however the pointer 'ptr' still exists,
//and is no longer pointing at anything which means it can be assigned a new reference
}
//--------------
//Example2 - Memory Leak Problems With Dynamic Variables
int main(){
int *ptr = new int;
ptr = new int; //MEMORY LEAK
//^Since pointer was already storing another int in heap, this would now be a memory leak as the location of int 1 is now lost,
//no way to recover, still exists, but cannot find
}
//--------------
//Example3 - Dynamic Arrays w/ Pointers
int main(){
int* a;
int arr[10];
a = arr;
//^Correct assignment of array to a pointer, no pass-by-reference needed since the array name itself functions as a pointer,
//therefore, basically this line is saying pointer 1 now equals pointer 2; pointer 1 is now pointing at pointer 2
int *ptr;
ptr = new int[10];
//^Assigning of a pointer to now point to an array with 10 indiices, this exists within the heap
ptr[6] = 42;
//^Assigning the value of the 6th index within the array that exists within the heap the value of 42
//Accessed like a regular array, no explicit dereference is needed since compiler already understands it to be a pointer,
//pointing to an array
delete [] ptr;
//^Keep in mind special syntax when deleting an array within a heap vs. a regular int
}
//--------------
//Example4 - Pointers & Functions
void newFunc(){
int *ptr;
ptr = new int;
//^Newly created allocation in heap, will exist continuously until deleted
}
int* ptrFunc(int* fuunc){
//perform operations here int* a;
return a;
//^Remember; don't have to use asterisk anywhere within return
}
//--------------
//Example5 - Illustrating Dynamic Objects
class IceCream{
public:
IceCream(int, char);
IceCream();
void get();
private:
int toppingCount;
char flavor;
};
int main(){
IceCream* iC1;
iC1 = new IceCream;
//^This would create new object of class IceCream and store it in allocated location in heap, using pointer 'iC1' to,
//keep track of location
//Conversly;
iC1 = new IceCream(1, 'c');
//^This creates object of class at place in heap, assigning values with the cooresponding constructor
//Similarly to how we did it in arrays, once pointer is assigned object, can just simply invoke functions on behalf of object;
iC1->get();
//^This is still acting on the object that iC1 is pointing to, pointer is simply calling function on behalf of it
}
/**/
//---------------------------------------------------
//SUMMARY
/*
- Dynamically Allocated Memory is controlled by the programmer and is stored addresses within the heap
- ALWAYS delete created portion in heap before reassignment of pointer, otherwise causes memory leaks
- Dynamic Memory Allocation can be applied to arrays, objects of classes, and any other facet discussed in pointers
- A lot of overlap between pointer notes and this
- Memory locations can be created within scope of function, still exists once created, will always exist within heap,
until ultimately deleted
*/
//pointers
//3/32/25
//Caleb Magill
//-----------------------------------------------------------
//NOTES
/*
Pointers
- Used to get and manipulate memory addresses of variables
- Every variable has a memory address
- Variables may be declared in two ways;
- Non-pointers
- Traditional way of declaring and use of variables
- Pointers (See Example1)
- Variables that stores the memory address of an EXISTING variable rather than data,
of that type
- Pointer variables should match type of variable who's location they're storing
- Pointers can be declared alongside non-pointer variables (See Example1)
- Referencing and Dereferencing
- Referencing is assigning a variable to a pointer for it to keep track of (Like in Example1),
- Dereferencing involves removing reference variable from pointer variable (See Example2)
- Dereferencing only applies to pointer variables
- Since every variable has a memory address, can do a pointer to a pointer (See Example2)
- Pointers and Constants (See Example3)
- A 'Pointer to a Constant' is a pointer that is declared as a const,
once assigned an object to point at, this object cannot be modified through,
the pointer, however, the pointer itself can be modified
- A 'Constant Pointer' is the opposite, whereas the object the pointer,
is pointing at can be modified through the pointer, while the pointer itself,
cannot be modified
- Just keep in mind importance of syntax, as these two are very easily confusable
- Pointers and Arrays (See Example4)
- Array name is a pointer, equivilent to "int(or whatever type)* const"
- A pointer may be assigned an array object
- Once pointer is assigned to array, default points to first index, however, through pointer arethmetic,
can point to other indicies
- Pointer Arithmetic
- Oh boy...
- Pointer arithmetic is the practice of incrimenting pointer variable assigned an array to point to other,
indicies of array, since it actually has access to full array
- Actually not as bad as it originally sounded, just concept of adding and subtracting values to pointer to change,
which index within array pointer is pointing at
- Null & Loose Pointers (See Example5)
- Loose pointers mean pointers that point to an invalid memory location...but it still exists... just don't do this
- Will compile but it is an issue and something you should try and avoid
- Dual-Functionality of the '*'
- The asterisk has multiple use-cases in the context of pointers; (See Example6)
- 1 = dereferencing the pointer, which then can be assigned a new memory location to point to
- 2 = accessing the stored value of the int in heap that the pointer is pointing to
- Pointers to Objects (See Example7)
- Pointers can be used to point toward objects
- Pointers, once pointing at object, can be used to access and perform operations within the context of the object,
that it is pointing at, the changes are reflected on the object being pointed at
- Basically with member functions, the pointer calls them on behalf of the object it is pointing at
*/
//-----------------------------------------------------------
//EXAMPLES
/**/
//Example 1 - Demonstrating the declaration of pointer variables in comparison to non-pointer
int main(){
int numberOfCats = 15;
//^Normal variable declaration, typical int that holds a value of that type
int *locationOf_numberOfCats = &numberOfCats;
//^Pointer variable declaration, specified by asterisk, will hold location of 'numberOfCats',
//variable, matches type of variable who's location it is holding
char catChar = 'k';
char *locationOf_catChar = &catChar;
//^Again notice the type of pointer matches that of the variable it is storing
//Note: the assignment for the pointer variable is call-by-reference to the variable rather than,
//by value, this is important as it is going to give location of that reference, not value
//CANNOT pass-by-value for pointer assignment, think, where would the pointer go?
int k, *l;
//^Can intermix declaration of pointer and non-pointer variables
}
//------------------
//Example 2 - Demonstrating Referencing and Dereferencing of Pointer Variables & Pointer-Pointer
int main(){
int r, *a, t;
//^Can intermix declarations of pointer and non-pointer variables
r = 15;
t = 8;
a = &r;
//Pointer 'a' is assigned memory address of variable 'r'
*a = t;
//Dereference operation followed by reassignment of pointer 'a',
//pointer 'a' now stores memory address of 't'
int notPointer = 5;
//Regular int variable declared
int *pointer = ¬Pointer;
//Pointer variable declared and storing memory address of 'notPointer' variable
int **pointerPointer = &pointer;
//^Pointer of a pointer variable declared, stores memory address of 'pointer',
//as remember, a pointer points to the memory location of a variable in memory,
//however, that pointer variable itself is stored in memory elsewhere, just points,
//meaning we can use another pointer, pointer-pointer to point to location of pointer
//i am going insane
}
//------------------
//Example 3 - Demonstrating the Relationship Between Pointers and Constants
int main(){
int *pointer;
int object = 5;
pointer = &object
//^Pointer is a variable that stores a memory address
//^Object is the variable the pointer is pointing at
//pointer to a constant
const int* ptr;
//'ptr' can be modified, however, the object it is pointing at,
//cannot be modified
int a = 10;
const int* ptr = &a;
*ptr = 5;//wrong
//^The object it is pointing at cannot be modified once it is declared
ptr++;//right
//^The pointer itself can however be modified
//constant pointer
int* const ptr;
//'ptr' cannot be modified, however, the object pointed to can,
//be modified
int a = 8;
int* const ptr = &a;
*ptr = 5;//right
//^Because the object the pointer is pointing at can be modified
ptr++;//wrong
//^On the other hand, the pointer itself cannot be modified
}
//------------------
//Example 4 - Demonstrating the Relationship Between Pointer and Arrays & Pointer Arithmetic
int main(){
int *p;
int a[50];
p = a;
//NOT "p = &a"
//^Because 'a', despite not being a pointer, functions similarly, therefore
//"p = &a" is attempting to assign all indicies and subsequent values of to pointer 'p',
//yet pointer 'p' can only hold one object/variable
//When assigning pointer like this, pointer automatically points to first index in array, index 0
//Pointer arithmetic
int *pointer;
int a[56];
pointer = a;
//^Automatically points to a[0]
pointer + 1;
//^Pointer now points to a[1]
pointer--;
//^pointer now points to a[0]
++pointer;
//^Also increments
}
//------------------
//Example 5 - Demonstrating Null & Loose Pointers
int main(){
int *ptr;
*ptr = 5;
//^Invalid memory location
//Not sure why it is valid vs. bottom example, basically this is called a loose pointer since it is not a valid memory location,
//but it still will compile...for some reason????
ptr = 5;
//THis is an error because type mismatch??????
}
//------------------
//Example 6 - Multiple Uses of Asterisk
int main(){
int* ptr;
int a = 5;
ptr = &a;
//^Assignes the pointer to point to the location of 'a'
//Recall "ptr = a" would just be assigning the arbitrary value of 5 to pointer which is arbitrary location
int* anotherPtr;
ptr = new int;
*ptr = 5;
//^Now the asterisk is used to access the value being stored by the newly created int in the heap
}
//------------------
//Example 7 - Pointers to Objects
class NewClass{
public:
NewClass(int, int, int);
NewClass();
int getClass(int);
void setClass();
private:
int a, b, c;
};
int main(){
NewClass newClass;
NewClass* classPtr;
classPtr = &newClass;
//^Now pointer 'classPtr' of type 'NewClass' is pointing toward object 'newClass'
classPtr->setClass();
//Any change that occurs from the pointer invoking functions reflects on the object being pointed at
}
/**/
//-----------------------------------------------------------
//SUMMARY
/*
- pointer variables must match the type of the variable that they store
- Can point to array and utilize pointer arithmetic to determine which index you want to point to
- Can create pointers to point to any type of data you want
*/
//Structures
//Caleb Magill
//3/18/25
//---------------------------------------------------
//NOTES
/*
Structures
- Group variables(of any type) together under one name
- Variables of struct are called "members"
- Aggregate construct
- Declared before main() function
- Can have a capitalized first letter in name
- Good practice to capitalize name of struct so not confused
- Cannot modify members of struct once they're declared (see Example3)
- So, use structs to store constant data
- Can create a complex struct (See Example5)
- This means putting a struct object within a struct
- Can do as much layering of this as you
- Structures may also store other variables such as arrays (See Example6)
- Structures can be used to perform functions (See Example 7)
- Two steps to using structures:
1 - defining the structure (see Example1)
2 - defining members of the created structure (see Example2)
- Can also assign values to struct instance on declaration (See Example8)
- DO NOT have access modifiers
- Keep it simple, structs are a lot more straigthforward than classes
- An instance of a struct can be assigned the same values to that of another,
instance of the same struct (See Example9)
- IMPORTANT NOTE; cannot directly compare instances of structs alone,
instead have to specify which member of two instances to compare to (See Example10)
- Reviewing call-by-reference vs. call-by-value, in context of structs (See Example11)
- Call-by-value means you are creating a copy of that struct and acting,
upon it in context of function, meaning values aren't actually manipulated of,
argument struct instance
- IMPORTANT DISTINCTION; if you add a return of the struct instance you,
passed as your argument, it WOULD update the values of the struct outside,
the function
- Call-by-reference means you are passing argument of actual memory address,
of instance of struct and member values meaning struct instance values are actually,
being modified
- Member Functions vs. Non-Member Functions (See Example12)
- Member functions are declared within struct declaration,
only real difference is how the function is called, member functions,
have access to pointer that allows them to be called as a member rather,
than a traditional function call
- Non-Member functions, declared outside of the struct declaration,
return type would be instance of struct passed or whatever variables you,
were to modify as apart of instance passed, doesn't have access to pointer,
so needs traditional invocation of function
- Structures & Arrays, here's where it starts to get messy
- Member variables of structs can be arrays, and accessing them,
would be the same as typical for any other member, accessing first the variable,
struct's name, the index you want to assign the value for, and then the,
value you want to assign it
- Can also declare an array of structs, hang on tight (See Example13);
- When declaring instance of array, can declare as an array of instances,
of structs, meaning multiple instances of struct will all be contained within,
single declared array, which is a single instance of the struct, ikr, from there,
can modify and assign values of members of each individual instance of struct,
contained within valid indicies of array
- Also important in context of functions as you can pass these arrays as arguments,
and the individual member variables within the indicies can be modified and manipulated
*/
//---------------------------------------------------
//EXAMPLES
/**/
//Example1
//Defining a struct
struct Student{
std::string name;
double gpa;
bool enrolled;
//^created variables in struct are called "members"
}
//----------
//Example2 (using struct definition from Example1)
int main(){
Student student1;
//^The 'student1' is a variable of "Student" struct,
//it will have its own name, gpa, and enrolled properties apart of itself
student1.name = "John";
student1.gpa = 3.2;
student1.enrolled = true;
//^So now defining values of members within variable "student1"
std::cout << student1.name;
//^When using variables of structure, don't have to,
//redeclare what struct they're apart of,
//(Just use student1 not Student.student1)
}
//----------
//Example3 (struct members remain constant)
struct Farm{
int wheatHarvested = 3600;
int carrotsHarvested = 6900;
int applesHarvested;
}
int main(){
Farm haul1;
haul1.wheatHarvested = 2000;
//^Not possible, as once a value is assigned,
//it cannot have its value redeclared
haul1.applesHarvested = 2000;
//^This works as it was not previously defined
}
//----------
//Example4 (struct default values)
struct Zoo{
int monkeyCount = 5;
//^Can assign default value, which means when declaring,
//variables of "Zoo" in main, wouldn't need to define
int zebraCount;
}
int main(){
Zoo.cincinnatiZoo;
cincinnatiZoo.zebraCount = 7;
std::cout << cincinnatiZoo.monkeyCount << endl;
//^Already assigned whatever default value member is,
//storing, would be 5
std::cout << cincinnatiZoo.zebraCount << endl;
}
//----------
//Example5 (Complex structure)
struct Address{
int houseNumber;
int zipCode;
}
struct Person{
int age;
int ssn;
Address address;
//^Creates an Address object defined within Person struct
}
int main(){
Person personl1;
person1.age = 18;
person1.ssn = 18939428348;
person1.address.houseNumber = 721;
//^Access address variable then the variable you want,
//to assign a value toward within that
person1.address.zipCode = 16066;
}
//----------
//Example6 (Structure with Array)
struct ExampleWithArray[
int a;
int b[5];
]
int main(){
ExampleWithArray ex1;
ex1.a = 5;
ex1.b[2] = 56;
//^Defining the third index of the array 'b''s value as 56
}
//----------
//Example7 (Structure as use of return)
struct Date{
int month;
int day;
int year;
}
Date setDate(int m, int d, int y){
Date tmp;
//^Defining object of "Date" struct
tmp.month = m;
tmp.day = d;
tmp.year = y;
//^Assigning values that are presumably passed in main loop
return tmp;
}
int main(){
Date projectDate
projectDate = setDate(10,31,2025);
}
//----------
//Example8 (Declaring struct instance values on declaration)
struct Date{
int day;
int month;
int year;
}
int main(){
Date birthday = {25, 4, 2006};
//^Instance of 'Date' struct called "birthday"
//^ variable values assigned in order,
//day = 25, month = 4, year = 2006
}
//----------
//Example9 (Assigning values of one instance of struct by use of another)
struct Date{
int month;
int day;
int year;
}
int main(){
Date independenceDay = {7, 4};
//^only assignes values of 'month' and 'day'
Date today = independenceDay;
//struct instance 'today' variables 'month' and 'day' will hold,
//same values as 'independenceDay' struct instance
}
//----------
//Example10 (Comparing two instances of structs member values)
struct Date{
int month;
int day;
int year;
}
int main(){
Date independenceDay = {7, 4};
Date today = independenceDay;
if(today.day == independenceDay.day){
//^Specifying which members to compare to of each struct instance
//In this case it would be true since both equal 4
}
if(today == independenceDay){
//^INVALID as you cannot directly compare two instances without,
//specifying a member for the comparison
//(Cannot compare two instances as a whole)
}
}
//----------
//Example11 (Reviewing call-by-reference vs call-by-value)
struct Date{
int day;
int month;
int year;
}
Date modifyDate(Date passedDate){
passedDate.day = 11;
passedDate.month = 6;
passedDate.year = 2005;
return passedDate;
//Excluding the return, since we called-by-value, the value modifications,
//wouldn't be updated outside the function, only exist within
//If we instead passed-by-reference, no need for a return as it will,
//already have updated the instance of the struct
}
int main(){
Date specialDay1 = {1, 12, 2009};
modifyDate(specialDate1);
//^Because of function, specialDate1's values would be updated outside,
//of the function due to the return
}
//----------
//Example12 (Member vs. Non-Member Functions)
struct Date{
int day;
int month;
int year;
//Member function
Date addDate(Date instance, int daysToAdd){
int currentDays = instance.days + daysToAdd;
return instance;
}
}
//Non-member function
Date changeDate(Date instance, int daysToAdd){
int currentDays = instance.days;
currentDays += daysToAdd;
return instance;
//Return type must match that of function type
}
int main(){
Date today;
//With member function;
today.addDate(3, 17);
//With non-member function;
changeDate(today, 3);
}
//----------
//Example13 (Structs and Arrays)
struct Date{
int day;
int month;
int year;
}
int main(){
Date newDate[3];
//^Instance of date declared with three instance structs contained,
//within indicies of 'newDate[]' instance declaration
newDate[2].day = 25;
//^Saying the third instance of struct within 'newDate' array,
//member variable 'day' will have a value of 25
}
//IK it's kind of scary and doesn't seem like there's any practical implementation,
//right now, but just keep the rule in mind for future reference
/**/
//Classes
//3/20/25
//Caleb Magill
//NOTES
/*
Classes
- implement the idea of Modular Code, meaning code that can be partitioned into parts
- vs. Spaghetti Code which means difficult to maintain & unstructred
- Functions more dynamically than structures as structures are fixed
- Just as structs, classes are declared before main() loop
- Defintion: aggregate construct combining like code and data
^Recall aggregate construct means stores multiple things
- Contains two main contents;
1 - member variables (called attributes)
2 - member functions (called methods)
^Recall 'member' is an entity stored within class or struct
- Member variables and functions are declared with class definition (See Example1)
- IMPORTAT: member functions can modify member variables without accecpting them as arguments
- Note Distinct naming convention for members vs. scalars (See Example2)
- A variable of type class is called an 'Object', Object contains members as previously discussed
- Similar to how struct objects are created
- The 'public:' and 'private:' are called access modifiers; (See Example3)
- Controls the way in which members of a class can be accessed
- public members can be accessed by member functions and outside with dot-sequence
- private members can only be accessed within member functions, not ouside with dot-sequence
- Can define memebrs as either, can have both within class
- IMPORTANT3: rule of thumb, make variable members private, variable functions (if needed) public
- Makes debugging easier
- Can prototype functions in class definition and define function later, called out-of-line definition
- Alternatively, can also just define function within class declaration rather than prototyping (See Example2)
- Mutators vs. Accessors (See Example4)
- A mutator is a member function that modifies the state of an object
- The 'state' is a term used to refer to current values of the members belonging to a class
- An accessor is a member function that doesn't modify the state of an object, only returns info
about its' state
- An accessor should be marked with 'const' upon declaration so compiler can identify
any accidential object state modification
- Similarly to structs, classes can also store members of other classes (See Example5)
- Same rules apply however, where private members of original class definition cannot be accessed,
as member of another class object
- Constructors; (See Example6)
- Kinda like a function, but not quite
- Has same name as class, is autmatically invoked upon creation of object
- Multiple constructors may be declared, difference is number of arguments, which one is invoked,
depends on number of arguments user chooses to pass when declaring object
- Default or void constructor is constructor that doesn't take any arguments
- Should create as many constructors as there are possible options for object member initialization
- Arrays can be declared as objects of class, function same as with structs
- Friend Functions; (See Example7)
- friend function is not considered a member of class it was declared with
- friend function is standalone function that can access private members of class within object
- Can be mutators or accessors
- Difference between member functions is that they can be declared like a normal function,
rather than the same rules of classes applying to them in their declaration, additionally,
can be used to compare members of two different objects of a class, something not normally achievable
*/
//-----------------------------------------------------------------
//EXAMPLES
/**/
//Example1 - Demonstrating standard declaration of class and methods(member functions) & attributes(member variables)
class Date{
public: //Access modifier
void set(int, int, int);
//^Remember; member functions can modify memebr variables without explicitly accecpting them as arguments
int getDay();
//^Function type int
int month_;
int day_;
int year_;
//^Note: variables members within class are typically named ending in an underscore to distinguish,
//from scalar variables. Scalar variable is a single-value variable typically used within scope,
//of function. Meaning variables named ending in underscore denote they exist outside scope of,
//simple loop
};
//^Need semicolon at end of class declaration
int main(){
Date newDate; //Standard declaration of object of class, object "newDate" of Date class
newDate.getDay();
}
//------------------------
//Example2 - Demonstrating method's access to attributes without accecpting as argument & in-line v. out-of-line
class Date{
public:
void set(int, int, int);
int getDay(int day1, int day2){ //In-line definition example
int month_ = day1;
int day_ = day2;
//^Can access attributes without needing any reference
return day_;
}
int getMonth(int, int); //Function prototype (if choosing to define out-of-line)
private:
int month_;
int day_;
int year_;
};
int Date::getMonth(int currMonth, int futureMonth){ //Out-of-line example
//^Don't forget two colons (access to reference from class)
int month_ = futureMonth;
//^Can access attributes without needing any reference
currMonth += month_;
return currMonth;
}
//------------------------
//Example3 - Demonstrating difference between public and private access modifiers
class Date{
public: //Public access modifier
void set(int, int, int);
int currDay_;
private: //Private access modifier
int month_;
int day_;
int year_;
};
int main(){
Date newDate; //Declaration of object 'newDate' of class Date
int a, b, c;
newDate.currDay_ = 25;
//Valid, as currDay_ is public member
newDate.month_ = 6;
//Invalid and error would return since private member
newDate.set(a, b, c);
//valid, and can be assigned, since functions can access attributes even if they're private members
}
//------------------------
//Example4 - Demonstrating difference between mutator and accessor functions
class Farm{
public:
int pigs_;
int animalCount(int, int, int); //Neither an accessor nor mutator //prototype
int updateAnimals(int, int, int); //Mutator, prototype function
int getPigs(int) const; //Accessor //prototype
//^Accessors should always be denoted by const in order to identify accidential state modification
int getCows(int cows) const {return cows;} //In-line definition
//Another indicator this is an accessor function
private:
int sheep_;
int goats_;
int cows_;
};
//Neither because it doesn't alter any values of members in context of object but still performs calculations
int Farm::animalCount(int currSheep, int currGoats, int currCows){
int tempTotal;
tempTotal = currSheep + currGoats + currCows;
int totalAnimals = pigs_ + tempTotal;
return totalAnimals;
}
//Mutator function because it modifies values of members (so within context of whichever object's state is modified)
int Farm::updateAnimals(int currPigs, int currSheep, int animals){
sheep_ += currSheep;
pigs_ += currPigs;
animals = pigs_ + sheep_;
return animals;
}
//Accessor function because it doesn't modify the state of the object which called it
int Farm::getPigs(int userInput) const{ //out-of-line definition, make sure to reinclude 'const'
return userInput;
}
//------------------------
//Example5 - Demonstrating use of class objects as members of another class
class Farm{
public:
int getAnimals(int animals, int, int) const {return animals;}
private:
int sheeps_;
int goats_;
int pigs_;
};
class Factory{
public:
int getTools(int toolTotal, int, int) const {return toolTotal;} //in-line definition
private:
int wrenches;
int hammers;
int saws;
Farm farm1;
//^Object of 'Farm' class stored and used within 'Factory' class
}
int main(){
Factory f1;
int a, b, c;
f1.farm1.getAnimals(a, b, c);
//Error, since this farm1 is private member
f1.getTools(a,b,c);
//Hypothetically, if getTools() used a member of object 'farm1', it could access it
}
//------------------------
//Example6 - Demonstrating constructor declaration and functionality
class Clock{
public:
int set(int, int, int);
Clock(int, int, int); //Constructor accecpting 3 arguments //1
Clock(int, int); //Another constructor, accecpting 2 arguments //2
Clock(); //Void or default constructor, accecpting no arguments //3
private:
int hours;
int minutes;
int seconds;
};
int main(){
Clock newTime(6, 3, 9);
//^Constructor 1 is invoked automatically since passed 3 arguments,
//values are assigned to attributes in order of declaration, so,
//hours = 6, minutes = 3, seconds = 9
Clock anotherTime;
//Constructor 3 is invoked here as no arguments were passed
Clock arrayTime[10];
//Constructor 3 is invoked here since array was used
}
//------------------------
//Example7 - Demonstrating use of friend functions
class Barn{
public:
int verify(int, int, int);
Barn(int, int);
Barn();
friend bool equal(const Barn&, const Barn&);
//friend function starts with 'friend' predicate
//As will see, functions just like any function prototype
private:
int chickens_;
int ducks_;
int goats_;
};
//Friend function is defined like a normal function would be, think of friend function definition,
//as just like a function prototype outside of any class
bool equal(const Barn& barn1, const Barn& barn2){ //Accessor function, though could do mutator
if(barn1.chickens_ == barn2.chickens_){
//^Can access private members of each object and compare,
//something you traditionally couldn't do with member functions
return true;
}
else{
return false;
}
}
int main(){
Barn b1, b2;
equal(b1, b2);
//^Invoking function like normal
}
/**/
//-----------------------------------------------------------------
//SUMMARY
/*
- Classes are more dynamic than structs
- Also a lot more rules
- Friend functions are more useful than you think
- Classes are a good way of organization
- Don't forget semicolon following {} of class declaration!
- Private methods can access private attributes
- Denoting variable in argument as constant means the variable passed will never change in value,
throughout the course of the function
- Denoting the function as constant means the member variables, attributes, will never be able to be,
modified within that function
*/
//-----------------------------------------------------------------
//theBigThree.cpp
//Caleb Magill
//4/15/25
//---------------------------------------------------
//NOTES
/*
- The Big Three
- Copy-Constructors (See Example1)
- Like a constructor in that, once created, and take argument of another existing object of,
the same class, will invoke that copy-constructor
- Copy-Constructor is especially useful with classes containing dynamic memory allocations;
- When using default constructor with attribute pointers, a new memory address from heap,
is assigned, and whatever default values of the default constructor will reflect on the newly,
created object
- Copy-Constructors just translate whatever values onto that newly initialized object, still,
also handling attribute pointers the same where it also assigns new address in heap and pointer,
points to that
- Overloaded Assignments (See Example2)
- Own constructor, called copy-assignment constructor, don't confuse with copy constructor,
and it has own unique syntax
- Functionality differs from Copy Constructor, as it has same intent, copying contents of one object,
to another, however, distinction is Copy Constructor can only be invoked upon initialization of new object,
overload assignment, using Copy-Assignmnet Constructor, is used when we want to copy values of already-existing,
object to another
- Can add Self-Assignment Protection to prevent an object from being reassigned whose values it already has
- Use keyword 'this' to signify invoking instance in comparison to argument instance and check whether,
they're equal
- The return type is "this*" which is just whatever the object is that invoked it, "return" compares by,
taking object that called it and comparing it to the argument that it passed with it, if all attributes of,
hold the same value, then these objects are interpreted as the same, else they are not
- Destructors (See Example3)
- Just as other two, is invoked implicitely once object goes 'out of scope'
- 'Out of Scope' means like if an object is created locally in a function;
- Given a class with pointers to dynamic memory locations, in heap, if created new instance within,
function, outside of function, this still exists, recall notes on Dynamic Memory Allocation
- Destructor is automatically invoked to remove that instance of the location in heap upon,
the exiting of the function or block of code it was created in, hence 'out of scope'
- Important Note: For dynamically allocated object, not as attributes of a class, there exists a built-in,
destructor that compiler implicitely invoked upon the scope being left, meaning such objects are already,
handled by compiler
*/
//---------------------------------------------------
//EXAMPLES
/**/
//Example1 - Demonstrating Everything Copy-Constructors
class Date{
public:
Date(); //default constructor
Date(const Date&); //Copy-Constructor
//^Uses 'const' because we don't want to modify values of argument object
//^Uses pass-by-reference since we want to get reference to reflect values onto object,
//that invoked it
private:
int day_;
int month_;
int year_;
int* ptr;
int size_;
};
void Date::Date(){
//^Recall default constructors are of type void
int scalarVariable = 12;
day_ = 5;
month_ = scalarVariable;
year_ = 2025;
size_ = 1;
ptr = new int[size_];
//^Creates a new place in heap to hold int's value, ptr is pointint to its location in heap
}
void Date::Date(const Date& alreadyExistingObj){
day_ = alreadyExistingObj.day_;
month_ = alreadyExistingObj.month_;
year_ = alreadyExistingObj.year_;
size_ = alreadyExistingObj.size_ + 1;
ptr = new int[size_];
//^Assignes unique memory address to store int and ptr points at int's location, size_ is updated
}
int main(){
Date obj1;
//^Default constructor would be implicitely invoked
//Do stuff to obj1 here
Date obj2(obj1);
//^Copy-constructor would be "implicitley" invoked
//Now current state of values of obj1 are assigned to obj2
}
//-------
//Example2 - Demonstrating Everything Overloaded Assignments
class Date{
public:
Date(); //default constructor
Date(const Date&); //Copy-Constructor
void operator= (const MyClass&); //Copy-Assignment Constructor
//^Keyword operator used
private:
int day_;
int month_;
int year_;
int* ptr;
int size_;
//Same Class exists as with last example, just additional inclusion of Copy-Assignment Constructor
};
void Date::operator = (const Date& dateObj){
day_ = dateObj.day_;
month_ = dateObj.month_;
year_ = dateObj.year_;
size_ = dateObj.size_ + 1;
ptr = new int[size_];
//^Same functionality as with copy-constructor, just now dealing with already-initialized objects
}
int main(){
Date obj1, obj2;
//Can do stuff with both obj1 & obj2
obj1 = obj2;
//Copy-Assignment Constructor invoked
}
//Copy-Assignment Constructor with Self-Assignment Protection
const Date::operator = (const Date& dateObj){
//^Now changing the type of the function to return an instance of object as it now optionally runs based on a condition
if(this != dateObj){
day_ = dateObj.day_;
month_ = dateObj.month_;
year_ = dateObj.year_;
size_ = dateObj.size_ + 1;
ptr = new int[size_];
}
return *this;
//Returning since it is optional whether the if() statement runs or not
}
//WOULD ALSO NEED TO CHANGE PROTOTYPE IN CLASS DEFINITION
//-------
//Example3 - Demonstrating Everything Destructors
class Date{
public:
Date(); //default constructor
Date(const Date&); //Copy-Constructor
void operator= (const MyClass&); //Copy-Assignment Constructor
void assignDayz(int dayz);
~Date();
private:
int day_;
int month_;
int year_;
int* ptr;
int size_;
};
//Out-of-Line definition
Date::~Date(){
delete ptr;
//^Assuming 'ptr' is pointing to int in heap
}
void Date::assignDayz(int dayz){
Date temp;
//^Creates a new object within scope of function, since includes pointer, and assuming we give the,
//pointer a variable to point at in heap, upon exit of function, this will still exist
//...proper functionality of function...
}
//^Upon exit of this function in callstack, destructor is automatically invoked to remove the partition,
//within the heap that the object 'temp' is storing
//This is because the object now went out-of-bounds
int main(){
Date obj1, obj2;
int dayCount = 3;
obj1.assignDayz(dayCount);
//^Within this function it will create new partition within heap of object 'temp' that we created,
//within the function
}
/**/
//---------------------------------------------------
//SUMMARY
/*
- Big Three are; Copy-Constructors, Assignment Overload, and Destructors
- Copy-Constructors
- Used to set state of object to values of already-existing object on declaration
- Assignment Overload
- Used to set state of already-existing object to that of another already-existing object
- Can use Self-Assignment Protection to prevent extraneous assignment
- Destructors
- Used to deallocate memory locations in heap once object has gone out of scope
*/
//File Input and Output
//3/10/25
//Caleb Magill
//NOTES
/*
Remember:
- C++ uses streams for input and output
- Streams are sequences of data that can be read (bytes for computers)
- Output steam is used to write data to a file
- Input stream is used to read data from a file
Why:
- Because taking input if needing 100 values from user terminal is impractical
- Because we can store and save data from terminal quickly into a file
Needed Include Lines:
- #include
- using std::ifstream; using std::ofstream; (fin and fout)
- Declaring file steam variables (yes you have to do this)
- ifstream fin; ofstream fout;
- this is because an opened file is considered an object (or variable) in the context of our script when we're performing operations on it
Always First Step:
- Need to connect to a file to perform any operations
Example(2):
fin.open("myFile.txt");
- fin is the file input stream variable
ifsteam fin("myFile.txt");
- can also declare and open file in one line
- Next step is to verify file is actually opened
Example:
if(fin.is_open()){}
- function that returs t/f bool if file is open
While File is Open:
- While file is open, you can read date from input stream and write data to output stream
- fin >> myVar;
- fout << myVar;
Last Step:
- Close the file
- fin.close();
- fout.close();
Side Notes:
- You can specify a file path with the file name so the computer will know which directory to look at (make sure it has permissions (if applicable) to read from destination)
- Output file path doesnt already have to exist, as you can write data to a new file
*/
//EXAMPLES
/**/
//Example 1
//String variable as file name
string fileName = "myFile.txt";
fin.open(fileName);
//^Given fin is variable for input stream
//Example 2
//Declarations & Setup
#include
using std::ifstream; using std::ofstream;
ifstream fin;
ofstream fout;
fin.open("myFile.txt");
fout.open("myOutputFile.txt");
//^All Required Setup
//Exapmle 3
//File-Reading Idiom
while(fin >> myVar){
//Perform whatever functions you want on input data from vile
}
//^Same can be done for ofstream
//Example 4
//Closing Files
fin.close();
fout.close();
//^Closes streams
//DONT FORGET THIS otherwise it is continuously open and can cause issues
/**/
//SUMMARY
/*
- Output file must not already exist
- Can only write to a new file, if file in destination with same name, it is overwritten
- Always close files after use
- Can use loops to more simply read and write data from files
- What data is readable/writable depends on type of file and what data is being retrieved
- You can store data retrieved normally through variables
*/
//Caleb Magill
//2/12/25
//Understanding Header and Linker process of cpp
//NOTES
/*
Only one cpp file can contain the main() loop
Protections for hpp file include;
#ifndef NAMEOFHEADER_HPP
^syntax;
- all caps
- _HPP as extension
#define NAMEOFHEADER_HPP
^same syntax applies
#endif
^last line of code
1. ifndef checks whenther the namespace is already defined
2. if not, then 2nd line will define the namespace
3. endif is always last line of hpp script
- Both external cpp file being referenced and main.cpp have to contain
#include "headerScript.hpp"
^name of external header file referenced
*/
//EXAMPLES
/** */
//ex. (externalFunc.hpp)
#ifndef EXTERNALFUNC_HPP
#define EXTERNALFUNC_HPP
void func1(int, char);
void func2(int, int);
#endif
//^ externalFunc.cpp will contain code of actual function
//SUMMARY
/*
- main.cpp has to contain;
#include "myHeader.hpp"
- header file must have three key lines;
1 - #ifndef MYHEADER_HPP
2 - #define MYHEADER_HPP
3 - #endif
- External cpp files must contain;
#include "myHeader.hpp"
*/