Welcome to the C++ Modules!
These modules are an introduction to Object-Oriented Programming in C++.
- Orthodox Canonical Form
- Public, Private, Protected Access Specifiers
- Deep copy & Shallow copy
- clone()
- Stack & Heap allocation
- Reference & Pointer
- const
- Floating-Point & Fixed-Point
- Inheritance
- Inheritance (operator=)
- Virtual Class
- Virtual Functions
- Type Casting
- Containers and Container Adapters
- Ford-Johnson Algorithm
- C++ Modules
These webpages provide a comprehensive resource for C++, containing all the essential information you need.
Implement the four required member functions:
Default constructor
Copy constructor
Copy assignment operator
Destructor
Constructor();
- A default constructor is a special member function of a class
that is called when an object is created without any arguments. - It initializes the object's data members,
to a default value (usually 0 or a specified value). - It's called automatically when you create an object of the class
without providing any explicit constructor arguments.
Constructor(const Constructor& value);
- A copy constructor is a member function that creates a new object
by copying the values of another object of the same class. - It's used to initialize a new object with the same data as an existing object.
Constructor& operator=(const Constructor& value);
- The copy assignment operator (operator=) is a member function
that allows you to assign the value of one object
to another object of the same class. - It's used to make a deep copy of one object into another.
- It is typically invoked when you use the assignment operator,
like obj1 = obj2, and ensures that obj1 now holds a copy of obj2's data.
❗NOTE:❗
obj1 = obj2:
Should work.
obj1 = obj2 = obj3:
Should work.
obj1 = obj1:
Should NOT work.
~Constructor();
- A destructor is a special member function that is called when
an object is destroyed or goes out of scope. - It is used to perform cleanup operations,
such as releasing resources or deallocating memory,
and is the opposite of a constructor.
The public, private, and protected access specifiers are used
to control the visibility and accessibility of class members
(variables & functions) within a class and its derived classes.
Members declared as public are accessible from anywhere in
the program, both within the class and outside the class.
They are typically used for the interface of the class,
which defines how the class can be used by external code.
Members declared as private are only accessible within
the class where they are defined.
They are not accessible from external code or derived classes.
Members declared as protected are accessible within the class
where they are defined and within derived classes.
They are not directly accessible from external code,
but they can be accessed by derived classes.
Example:
class MyBaseClass
{
protected:
int protectedVar;
void protectedFunction();
};
class MyDerivedClass : public MyBaseClass
{
public:
void accessProtected()
{
protectedVar = 42; // Accessible in derived class.
protectedFunction(); // Accessible in derived class.
}
};
- A deep copy creates a new object that is a duplicate of the original object,
and it recursively copies all the objects referenced by the original object. - Deep copy ensures that the copy is fully independent of the original object.
- Any changes made to the copied object or its internal data won't affect the original object.
- A shallow copy, creates a new object that is a copy of the original object,
but it doesn't recursively copy the objects referenced by the original. - Instead, a shallow copy maintains references to the same objects as the original.
- Shallow copies are often faster and use less memory than deep copies
because they don't replicate all the data and objects. - Any changes in the copied object can affect the original object and vice versa.
In summary, the key distinction is that a deep copy creates a completely independent duplicate,
while a shallow copy shares some of the underlying data with the original object.
The clone()
method is fundamental in creating precise replicas of objects.
In object-oriented programming, this method generates a new object that retains
the same state as the original. However, it's essential to note that clone()
might not uniformly create new instances for every data component within the object.
-
A basic clone typically replicates the top-level object and its references.
In contrast, a deep copy duplicates the entire structure, including nested objects,
ensuring an independent duplicate. -
Changes made in one object reflect in both.
- The heap is created on the heap using dynamic memory allocation,
typically through thenew
operator in C++. - This means that the memory for the heap object is allocated on the heap,
and you need to manually deallocate it when you're done with it usingdelete
. - Heap-allocated objects have a longer lifetime and exist
until explicitly freed by the programmer. Example:
int main()
{
Heap *newHeap = new Heap("Bob");
delete newHeap;
return 0;
}
- Stack-allocated objects are automatically managed by the program's execution stack.
- They have a shorter lifetime and are typically limited to the scope in which they are defined.
- When the scope ends (e.g., when the function returns),
the stack-allocated objects are automatically destroyed. Example:
int main()
{
Stack newStack = Stack("Bob");
return 0;
}
- 🟣
Pointers
are declared using the*
symbol - (int* ptr;),
and they need to be dereferenced to access the value they point to*ptr = 42;
. - 🟠
References
are declared using the&
symbol - (int& ref = someInt;),
and you access the referenced value directly using the reference variableref = 42;
.
- 🟣
Pointers
can be declared without initialization and can be assigned a value later. Or can be assigned a special value,nullptr
, to indicate that they don't point to any valid object.
This allows for the representation of the absence of a value. - 🟠
References
must be initialized when declared, and once initialized,
they cannot be changed to refer to a different object.
This enforces that references always refer to a valid object.
-
const int fixed()
: This declares a member function fixed that returns a const int,
indicating that the integer returned by the function is constant and cannot be modified. -
int fixed(const int x)
: This declares a member function fixed that takes a constant integer parameter x,
meaning that x cannot be modified within the function. -
int fixed() const
: This declares a member function fixed that is marked as const,
indicating that the function can be called on const objects of the class and ensures that
the function does not modify the state of the object it is called on.
- ◻️
Floating-Point:
Numbers offer a variable level of precision.
They use a fixed number of bits for the mantissa and another set of bits for the exponent.
This allows them to represent a wide range of values but with varying precision. - ◼️
Fixed-Point:
Numbers have a fixed number of decimal places.
This means you specify a fixed precision for your numbers, and all numbers have the same precision.
- ◻️
Floating-Point:
Numbers can represent a wide range of values,
both very small (close to zero) and very large, thanks to the exponent. - ◼️
Fixed-Point:
The range is constrained to a predefined interval,
determined by the number of bits allocated for the integer and fractional parts.
-
◻️
Floating-Point:
The number does not reserve a specific number of bits for the integer part
or the fractional part. Instead it reserves a certain number of bits for the number (the mantissa or significand)
and a certain number of bits to say where within that number the decimal place sits (the exponent).
~For example:
A floating point number that took up 10 digits with 2 digits reserved for the exponent might represent:
Largest value:9.9999999e+50
(10^50),
Smallest non-zero value:0.0000001e-49
(10^-49)9.9999999e+50 ^^^^^^^^^^^^^ - Significand (9.9999999) - Exponent (50)
-
◼️
Fixed-Point:
The number has a specific number of bits (or digits) reserved for
the integer part (the part to the left of the decimal point) and a specific number of bits reserved for
the fractional part (the part to the right of the decimal point).
No matter how large or small your number is, it will always use the same number of bits for each portion.
~For example:
If your fixed point format was in decimal IIIII.FFFFF then:
Largest number -99999.99999
,
Smallest non-zero number -00000.00001
99999.99999 IIIII.FFFFF ^^^^^^^^^^^ - Fractional digits (FFFFF) - Integer digits (IIIII)
- ◻️
Floating-Point:
Suitable for scientific calculations, simulations, and applications
where a wide range of values and precision is required. - ◼️
Fixed-Point:
Suitable for applications where you need a consistent level of precision
and don't want to deal with the variability and potential rounding errors of floating-point numbers.
Floating-point represent real numbers (numbers with decimal points).
A Floating-point number is composed of two main parts.
Mantissa:
The first part is called the "mantissa", which stores the significant digits of the number.
It represents the actual value you want to work with.Exponent:
The second part is the "exponent", which indicates the scale or magnitude of the number.
It shows where the decimal point should be placed in the mantissa.Sign:
Floating-point numbers can be positive or negative,
so there's also a sign bit to represent the sign of the number.
- Floating-point numbers include both float and double.
- Float and double are standard floating-point data types,
designed to represent real numbers with variable precision.
- Fixed-point numbers don't have predefined data types.
- Fixed-point numbers are typically implemented using integer data types (e.g., int, long)
with a specific interpretation of the position of the decimal point.
Inheritance is a fundamental concept in object-oriented programming (OOP), and it plays a central role in C++.
-
It allows you to create a new class (derived or child class) based on an existing class (base or parent class).
-
Inheritance enables the child class to inherit the properties and behaviors
(data members & member functions) of the parent class, which promotes code reuse and the creation
of a hierarchical structure in your program. -
In C++, there are different types of inheritance, including
single inheritance
,multiple inheritance
, andmultilevel inheritance
.
Single inheritance is the simplest form of inheritance and means that a derived class inherits from a single base class.
~For example:
The Dog
class derives from the Animal
class and inherits the eat
method.
class Animal // Base class
{
public:
void eat();
};
class Dog : public Animal // Derived class
{
public:
void bark();
};
Multiple inheritance allows a derived class to inherit from multiple base classes.
In other words, a single derived class can have attributes and behaviors from more than one parent class.
~For example:
The Child
class inherits both function1
from Parent1
and function2
from Parent2
class Parent1 // Base class
{
public:
void function1();
};
class Parent2 // Base class
{
public:
void function2();
};
class Child : public Parent1, public Parent2 // Derived class
{
public:
void childFunction()
{
function1();
function2();
}
};
❗ For more insights into multiple inheritance, you can explore detailed explanations
on the subject at Geeksforgeeks.
❄️ Option A:
In this implementation, you perform a deep copy by manually copying each member variable
from the source object to the target object.
This approach ensures that the two objects are entirely independent of each other.
For example:
ClassA& ClassA::operator=(const ClassA& value)
{
// Perform a deep copy
_name = value._name;
_points = value._points;
_energy = value._energy;
_damage = value._damage;
return *this;
}
❄️ Option B:
In this implementation, the assignment operator delegates the assignment operation
to a base class
, BaseClass, by calling its assignment operator.
This is commonly used when a derived class
DerivedClass inherits from a base class BaseClass.
By invoking the base class's assignment operator, you effectively reuse
the assignment logic defined in the base class.
For example:
DerivedClass& DerivedClass::operator=(const DerivedClass& value)
{
BaseClass::operator=(value);
return *this;
}
The key difference:
Option A:
manually copies each member, making it a deep copy.Option B:
relies on the base class's assignment operator to handle the assignment, which can simplify the code.
In object-oriented programming, a virtual base class
is a base class that
is declared as "virtual" in a class hierarchy.
This concept is used to address issues that can arise in multiple inheritance scenarios,
particularly when you have a class hierarchy with diamond-shaped inheritance patterns.
You have a class hierarchy like this: (diamond-shaped inheritance pattern)
BaseClass_⚫ | BaseClass_⚫ --> is the base class.
/ \ |
/ \ |
/ \ |
/ \ |
Class_🔴 Class_🔵 | Class_🔴 inherit from BaseClass_⚫ --> is a derived class, BUT a base class for Class_🟡.
\ / | Class_🔵 inherit from BaseClass_⚫ --> is a derived class, BUT a base class for Class_🟡.
\ / |
\ / |
\ / |
Class_🟡 | Class_🟡 inherits from both Class_🔴 and Class_🔵 --> is a derived class.
- The issue with this setup is that when you create an instance of
Class_🟡
, it inheritsBaseClass_⚫
from bothClass_🔴
andClass_🔵
. - This can lead to ambiguity and problems when trying to access members (variables or functions) from BaseClass.
To resolve the diamond inheritance problem and ensure that there's only one instance of the BaseClass_⚫
shared among Class_🔴
and Class_🔵
, you should use virtual inheritance.
class BaseClass_⚫
{
// ... (rest of the code)
};
class Class_🔴 : virtual public BaseClass_⚫ // Use virtual inheritance
{
// ... (rest of the code)
};
class Class_🔵 : virtual public BaseClass_⚫ // Use virtual inheritance
{
// ... (rest of the code)
};
class Class_🟡 : public Class_🔴, public Class_🔵
{
// ... (rest of the code)
};
- Usage: A virtual function in a base class is a function that can be overridden in derived classes.
- Implementation: It has an implementation in the base class, but it can be overridden in derived classes to provide a different implementation.
- Example: In a Shape base class with a virtual function draw(), various shapes like Circle and Square can override this function to draw themselves differently while sharing the same base function name.
"It's handy when you want different versions of something in your program to behave slightly differently."
Virtual Function Example:
// Base class
class Shape
{
public:
virtual void draw() // Virtual function - has a function body
{
std::cout << "Drawing a shape." << std::endl;
}
};
// Derived classes
class Circle : public Shape
{
public:
void draw() // draw()-Circle will overwrite draw()-Shape
{
std::cout << "Drawing a circle." << std::endl;
}
};
class Square : public Shape
{
public:
void draw() // draw()-Square will overwrite draw()-Shape
{
std::cout << "Drawing a square." << std::endl;
}
};
int main()
{
Circle circle;
Square square;
Shape* shape1 = &circle;
Shape* shape2 = □
// Calls the overridden draw functions
shape1->draw(); // Output: "Drawing a circle."
shape2->draw(); // Output: "Drawing a square."
return 0;
}
Classes containing pure virtual functions
are known as abstract classes
.
Abstract classes cannot be instantiated directly; they are meant to be inherited by derived classes.
- Usage: A pure virtual function in a base class is a function that must be overridden in derived classes; it is declared in a base class but has no implementation in the base class (no function body).
- Implementation: It has no implementation in the base class and must be overridden by derived classes.
It's a function declared with = 0virtual void func() = 0;
. - Example: In a Vehicle base class with a pure virtual function start(), all derived vehicles like Car and Bike must implement their specific way of starting the vehicle. They cannot be instantiated directly because the base class lacks an implementation for this critical functionality.
Pure Virtual Function Example:
// Base class
class Vehicle
{
public:
virtual void start() = 0; // Pure virtual function - has no function body - no implementation
};
// Derived classes
class Car : public Vehicle
{
public:
void start() // Implementation of the pure virtual function for Car
{
std::cout << "Car started." << std::endl;
}
};
class Bike : public Vehicle
{
public:
void start() // Implementation of the pure virtual function for Bike
{
std::cout << "Bike started." << std::endl;
}
};
int main()
{
Car myCar;
Bike myBike;
Vehicle* vehicle1 = &myCar;
Vehicle* vehicle2 = &myBike;
// Calls the overridden start functions
vehicle1->start(); // Output: "Car started."
vehicle2->start(); // Output: "Bike started."
return 0;
}
Key Distinction: virtual function has a default implementation in the base class but can be overridden, while a pure virtual function has no implementation in the base class and must be overridden in the derived classes to provide functionality.
🌸 static_cast: - used on types
It implicit conversions between types (such as int to float, or pointer to void*).
It is unnecessary when casting upwards (towards a base class),
but when casting downwards it can be used as long as it doesn't cast through virtual inheritance.
🏵️ dynamic_cast: - used on classes
It's used to convert pointers or references of a base class to pointers or references of a derived class.
Works only with pointers or references to polymorphic classes (i.e., classes containing at least one virtual function).
The dynamic_cast will seek out the desired object and return it if possible.
dynamic_cast<Derived*>(basePointer)
-> if the cast fails, it will return nullptr.dynamic_cast<Derived&>(baseReference)
-> if the cast fails, it will throw std::bad_cast, (use try/catch)
It doesn't work if you aren't using virtual inheritance and it will always fail to travel through protected or private inheritance (can only go through public inheritance).
💮 const_cast:
Can be used to remove or add const to a variable.
🌼 reinterpret_cast:
It turns one type directly into another, such as casting the value from one pointer to another, or storing a pointer in an int, or all sorts of other nasty things.
Think of a vector like a resizable array. You have a series of numbered slots, and you can put items in any of those slots. It's like having a list of boxes where you can add more boxes as needed, and you can access any box directly by its number.
- Dynamic array that resizes itself as needed.
- Efficient for random access and appending elements.
Imagine a long tube with openings at both ends. You can put things in or take them out from either end. It's like having a line of storage containers where you can add or remove items from both the front and the back.
- Allows efficient insertion and deletion at both ends.
- Similar to a tube with openings at both ends.
Picture a chain where each link is connected to the next one. You can add or remove links anywhere in the chain. It's like having a necklace where you can add or remove beads at any position along the chain.
- Doubly linked list allowing efficient insertion and deletion anywhere in the list.
- Imagine a chain where each link is connected to the next one.
- Singly linked list with less memory overhead.
- Similar to a list but with single links.
- Static array with a fixed size.
- Used when the size is known at compile time.
Think of a set like a collection of unique items. Each item in the set is distinct, and there are no duplicates allowed. It's like having a collection of different-colored marbles where you can't have the same color marble twice.
- Collection of unique keys, sorted according to a specified comparison criterion.
- Like a collection of distinct marbles.
- Collection of keys that can have duplicates, sorted according to a specified comparison criterion.
- Similar to a set but allows duplicates.
Picture a dictionary where each word (key) has a corresponding definition (value). You can look up a word and find its definition quickly. It's like having a phone book where you can find a person's phone number by looking up their name.
- Collection of key-value pairs with unique keys, sorted according to a specified comparison criterion.
- Similar to a dictionary with words and definitions.
- Collection of key-value pairs where keys can have duplicates, sorted according to a specified comparison criterion.
- Similar to a map but allows duplicate keys.
- Collection of unique keys, not sorted but with constant-time average complexity for most operations.
- Useful for fast lookup without sorting.
- Collection of keys that can have duplicates, not sorted but with constant-time average complexity for most operations.
- Similar to an unordered set but allows duplicates.
- Collection of key-value pairs, not sorted but with constant-time average complexity for most operations.
- Similar to a map but without sorting.
- Collection of key-value pairs where keys can have duplicates, not sorted but with constant-time average complexity for most operations.
- Similar to an unordered map but allows duplicate keys.
Imagine a stack of plates in a cafeteria. You put one plate on top of another, and when you need to remove a plate, you take the top one off. In programming, a stack works similarly. You add items to the top of the stack, and the last item you added is the first one you take out. Think of it like a pile of books where you can only add or remove books from the top of the stack.
- Last In, First Out (LIFO) data structure.
- Items are inserted and removed from one end.
- Useful for backtracking, recursion, and expression evaluation.
Picture a line at a movie theater. People join the line at the back and leave from the front as they enter the theater. In programming, a queue operates the same way. You add elements to the back of the queue and remove them from the front. It's like waiting in line for a rollercoaster ride where the first person who joined the line gets on the ride first.
- First In, First Out (FIFO) data structure.
- Elements are inserted at the rear and removed from the front.
- Used in scheduling, breadth-first search, and task management.
Think of a priority queue as a line where people with VIP passes go first, regardless of who arrived first. Each item in a priority queue has a priority, and the item with the highest priority gets removed first. It's like boarding a plane where first-class passengers (highest priority) board before economy class (lower priority).
- Queue with elements dequeued based on their priority.
- Highest priority elements are dequeued first.
- Often implemented using heaps.
LINK:
- 🟪 Springer Link (Helps with the JacobSthal sequense 'Step5 & Step6')
- 🟩 Link from the image (VERY USEFUL!)
Module 00:
(Topics: Namespace, class, member functions, and some basic stuff...)
These exercises provide a range of practical skills and knowledge for working with C++ and building more complex programs, from basic string manipulation to object-oriented programming and debugging.
This exercise is about string manipulation and basic C++ program structure.
You'll learn or practice:
- Command-line argument handling in C++.
- Converting strings to uppercase.
- Creating a simple C++ program structure with Makefile.
This exercise focuses on creating a simple phonebook program in C++ and using classes.
You'll learn or practice:
- Object-oriented programming (OOP) concepts, including class design and encapsulation.
- Input and output in C++, including formatting output.
- Command-line interaction and implementing basic command parsing.
- Memory management without dynamic allocation.
In this exercise, you are tasked with recreating a missing source file based on provided header files and a log file.
You'll learn or practice:
- Reverse engineering C++ code from header files and expected output.
- Debugging and fixing C++ code to match specified requirements.
- Understanding code functionality and relationships between different code files.
Module 01:
(Topics: New, pointers to members, references, switch statement, Memory allocation)
Each of these exercises in the C++ module teaches you different aspects of C++ programming, including object-oriented programming, memory management, file handling, and more.
This exercise covers the fundamentals of object-oriented programming in C++ and memory management.
You'll learn or practice:
- Creation of a simple class with private attributes and member functions.
- Memory management, including object creation on the stack and heap.
- Practice with C++ class constructors and destructors.
This exercise focuses on creating pointers to objects and arrays.
You'll learn or practice:
- Allocation and management of memory for an array of objects.
- Usage of pointers to objects and arrays.
- Initialization and utilization of objects within an array.
This exercise explores pointers and references in C++ through string manipulation.
You'll learn or practice:
- Understand the concept of pointers and references in C++.
- Practice printing memory addresses and values of variables, pointers, and references.
NOTE:
- (*PTR) can change the value and the address without influencing the original variable.
- (&REF) can change the original value.
In this exercise, you'll work with classes and objects, emphasizing constructors and object interactions.
You'll learn or practice:
- Learn about classes and objects in C++.
- Practice using constructors to initialize objects.
- Experience with object composition and interactions.
NOTE:
- For HumanA, Weapon (&REF) is required to/cannot be NULL; it always needs a weapon.
- For HumanB, Weapon (*PTR) can be NULL, allowing you to call attack() without setting the Weapon.
This exercise involves file handling and string manipulation, requiring you to recreate a missing source file
You'll learn or practice:
- Handling files in C++ without using C file manipulation functions.
- String manipulation and replacement.
- Error handling and input validation.
NOTE:
Take care of:
$ ./replace infile "" "hello"
$ ./replace infile "bee" "bee"
$ ./replace infile "bee" "been"
$ ./replace wrong_infile "hello" "Hello"
In this exercise, you are tasked with recreating a missing source file based on provided header files and a log file.
You'll learn or practice:
- Usage of pointers to member functions in C++.
- Implementation of a logging system with different log levels.
This exercise focuses on program behavior control using the switch statement and log filtering.
You'll learn or practice:
- Application of the switch statement in C++.
- Filtering and displaying messages based on log levels.
- Gaining experience in controlling program behavior using conditional statements.
Module 02:
(Topics: Ad-hoc polymorphism, operator overload and Orthodox Cononical classes)
This exercises provide a structured progression of learning, starting from building a basic class and gradually extending its capabilities to solve more complex problems in the context of geometric calculations and computer graphics.
This program introduces you to the Orthodox Canonical Form.
You'll learn or practice:
- Create a class representing fixed-point numbers in Orthodox Canonical Form.
Default constructor
Copy constructor
Copy assignment operator
Destructor
- You need to implementing member functions and operator overloading in C++.
You expand the functionality of your Fixed class to handle various constructors and conversions.
You'll learn or practice:
- You'll deal with operator overloading, including overloading the insertion (<<) operator for output stream.
- You will learn how to convert between fixed-point numbers and floating-point numbers or integers.
This exercise builds on the Fixed class by overloading comparison operators and arithmetic operators.
You'll learn or practice:
- You'll implement the 6 comparison operators:
>
,<
,>=
,<=
,==
and!=
. - You'll implement the 4 arithmetic operators:
+
,-
,*
, and/
. - You'll implement
(++i)
pre-increment,(i++)
post-increment,(--i)*
pre-decrement and(i--)
post-decrement operators. - You need to create static member functions and use them for finding the minimum and maximum values among fixed-point numbers.
In this exercise, you apply the concepts you've learned in the previous exercises to solve a real-world problem, which involves checking if a point is inside a triangle.
You'll learn or practice:
- You'll implement a function that performs geometric calculations to determine if a point is inside a triangle using Binary Space Partitioning (BSP).
- This exercise reinforces your understanding of classes, operators, and geometric calculations in C++.
Module 03:
(Topics: Inheritance)
This exercises offers a structured learning journey, providing insights into the mechanics of inheritance and the nuances of multiple inheritance.
This exercise guides you in creating the ClapTrap
class, which incorporates private attributes with predefined default values and public member functions.
You'll learn or practice:
- Defining constructors and a destructor.
- Managing class attributes.
- Displaying informative messages during the class's operations.
This exercise introduces the derived class ScavTrap
, inheriting from the base class ClapTrap.
You'll learn or practice:
- Customizing and overriding member functions within the derived class.
- Maintaining the correct order of construction and destruction.
- Expanding the class with new attributes and functionality.
This exercise expands your knowledge by creating another derived class, FragTrap
, which inherits from ClapTrap.
You'll learn or practice:
- Distinguishing the derived class from the base class with custom construction and destruction messages.
- Ensuring proper class inheritance and conducting comprehensive testing.
In the final exercise, you'll create a hybrid class named DiamondTrap
that inherits from both FragTrap and ScavTrap.
You'll learn or practice:
- Multiple inheritance, allowing a class to inherit attributes and functionality from two different base classes.
- Hndling the intricacies of maintaining the ClapTrap subobject and adding additional unique features.
Module 04:
(Topics: Subtype polymorphism, abstract classes, interfaces)
This module focuses on mastering object-oriented concepts such as classes, inheritance, polymorphism, memory management, and abstract classes/interfaces.
Each exercise progressively builds upon the previous one, introducing new challenges and reinforcing concepts.
In this exercise you need to implements basic polymorphism.
You will make a Animal base class with Dog and Cat derived classes,
for each class you need to implementing makeSound().
Introduces WrongCat inheriting from WrongAnimal to demonstrate overriding behaviors.
You'll learn or practice:
- Polymorphism: Implementing inheritance, overriding functions, and utilizing base class pointers for derived class objects.
- Inheritance: Creating base and derived classes (Animal, Dog, Cat) to showcase inheritance relationships.
- Virtual Functions: Implementing and utilizing virtual functions (makeSound()).
This exercise focuses on memory allocation, deallocation, ensuring deep copying, and managing object destruction through inheritance. You'll handle dynamic memory for objects (like the Brain class), ensure proper deep copying for derived classes (Dog, Cat), and implement destructors to prevent memory leaks.
You'll learn or practice:
- Memory Allocation and Deallocation: Managing memory for objects with dynamic memory (Brain class).
- Deep Copy: Ensuring objects of derived classes (Dog, Cat) are properly deep-copied.
- Destructors: Implementing proper destructors to prevent memory leaks.
This exercise involves preventing direct instantiation of the base class Animal by creating an abstract class.
You'll learn or practice:
- Abstract Classes: Creating an abstract base class (Animal) to prevent direct instantiation.
- Class Design: Ensuring functionality remains intact while preventing direct instantiation.
Implement interfaces using pure abstract classes and recap concepts.
You'll learn or practice:
- Pure Abstract Classes: Implementing interfaces using pure abstract classes (AMateria, ICharacter).
- Interface Design: Designing interfaces and concrete classes that adhere to them (Ice, Cure).
- Memory Management with Interfaces: Managing object interactions and memory when using interfaces.
Module 05:
(Topics: Repetition and Exceptions)
These exercises are about repetition and exceptions (throw exceptions), learing about try-catch blocks.
This exercise provides hands-on experience in designing a class with exception handling.
You'll learn or practice:
- Learn to implement custom exceptions (GradeTooHighException and GradeTooLowException) to handle invalid grade scenarios during the instantiation and grade modification of a Bureaucrat.
- Gain practical experience in using try and catch blocks to handle exceptions, ensuring robust error management in the program.
This exercise provides practical experience in designing classes that interact with each other, handling exceptions, and ensuring proper member function implementation.
You'll learn or practice:
- Practice throwing and catching exceptions in the context of class methods, understanding how to propagate errors appropriately.
- Understand how objects of different classes (Form and Bureaucrat) interact with each other through member function calls.
- Initialization and utilization of objects within an array.
This exercise provides hands-on experience in designing and implementing abstract classes, handling exceptions in class hierarchies, and ensuring proper collaboration between classes.
You'll learn or practice:
- Practice designing an abstract class (AForm) with private attributes, ensuring that it serves as a base class for concrete form implementations.
This exercise provides hands-on experience in designing a utility class, working with dynamic object creation, pointers, and error handling.
You'll learn or practice:
- Practice designing a utility class (Intern) with a specific purpose—creating forms based on given parameters.
- Learn to dynamically create objects (forms) within the makeForm() function, returning a pointer to the created object.
- Understand the use of pointers to manage and manipulate dynamically allocated objects, specifically in the context of creating and returning form objects.
- Finding alternatives to if/elseif/else forests for efficient and maintainable code.
Module 06:
(Topics: C++ casts)
These exercises will teach you different typecast methods (static_cast, dynamic_cast, reinterpret_cast), how to implement them.
This exercise provides practical insights into designing a class with static methods, handling different scalar types.
You'll learn or practice:
- Implement the conversion of a string representation to various scalar types (char, int, float, double).
- Practice validating and handling scenarios where a conversion may not make sense or overflows.
This exercise provides hands-on experience in handling memory addresses, implementing serialization and deserialization logic.
You'll learn or practice:
- Learn to implement a class (Serializer) with a static method (serialize) that converts a pointer to an unsigned integer (uintptr_t).
- Develop the counterpart static method (deserialize) that converts an unsigned integer back to a pointer.
- Learn about the potential risks and considerations when converting between pointers and unsigned integers.
This exercise provides practical exposure to polymorphism, virtual functions, and type identification without using the typeinfo header.
You'll learn or practice:
- Creating a function that prints the actual type of the object pointed to by a base pointer (Base)*.
- Creating a function that prints the actual type of the object referenced by a base reference (Base&).
Module 07:
(Topics: C++ templates)
Each of these exercises in the C++ module teaches about how to use and implement templates functions and classes.
This exercise provides a practical understanding of generic programming, enabling the creation of versatile functions that work seamlessly with different data types.
You'll learn or practice:
- Writing functions that can be called with any type of argument.
- Gaining proficiency in the syntax and structure of function templates.
This exercise emphasizes generic programming, showcasing how a single function template can be applied to arrays of different types.
You'll learn or practice:
- Practice dealing with memory addresses and understanding the nuances of working with arrays.
- Gain proficiency in using pointers to access and manipulate elements within an array.
- Understand how to pass a function as a parameter to your template, allowing for adaptable and versatile array processing.
This exercise provides a comprehensive understanding of class templates in C++, focusing on dynamic memory allocation, proper implementation of constructors and operators, and ensuring safe array access through exception handling.
You'll learn or practice:
- Practice creating a class template (Array) capable of storing elements of any type (T).
- Learn how to allocate memory dynamically using the new[] operator for flexible array sizing.
- Implement error handling by throwing an std::exception.
- Learn to implement different constructors and operators.
Module 08:
(Topics: Templated containers, iterators, algorithms)
These exercises will teach you about containers and iteration through them.
This exercise encompasses template programming, STL containers and algorithms, error handling.
You'll learn or practice:
- Practice working with STL containers and applying algorithms like 'std::find' to search for elements.
- Gain experience in error handling by deciding between throwing exceptions or returning error values.
This exercise provides hands-on experience in exception handling, class design and implementation, algorithmic problem-solving, working with iterators.
You'll learn or practice:
- Managing the storage of elements, preventing overflows, and calculating spans.
This exercise provides hands-on experience in enhancing and extending standard containers, implementing iterators, working with C++ standard library features, and testing the modified container against other alternatives.
You'll learn or practice:
- Learn how to extend the functionality of an existing container (std::stack in this case) to make it iterable.
- Gain experience in implementing iterators for a container class.
- Practice using iterators to traverse and access elements within a container.
Module 09:
(Topics: STL)
This module introduces different containers and their applications through practical exercises.
Container: 'map'
This exercise involves creating a program to determine the value of a specific amount of bitcoin on a given date.
You'll learn or practice:
- File handling for retrieving information.
- Understanding the 'map' container.
Container: 'stack'
Explore Reverse Polish Notation and implement it in a program.
You'll learn or practice:
- Understanding the mechanics of Reverse Polish Notation.
- Understanding the 'stack' container.
Container: 'vector' & 'list'
Implement the Ford-Johnson algorithm to sort a sequence of positive integers.
You'll learn or practice:
- Understanding the Ford-Johnson algorithm and Jacob Sthal sequence.
- Implementing algorithms across different containers.
- Comparing the performance characteristics of list and vector containers.