This module focuses on key object-oriented programming concepts in C++: polymorphism, abstract classes, and interfaces. These concepts are fundamental to creating flexible and maintainable code.
- Exercise 00: Polymorphism
- Exercise 01: I don't want to set the world on fire
- Exercise 02: Abstract class
- Exercise 03: Interface & recap
- Polymorphism: Runtime behavior selection through virtual functions
- Abstract Classes: Base classes with pure virtual functions
- Interfaces: Abstract classes with only pure virtual functions
- Virtual Functions: Functions that can be overridden by derived classes
- Memory Management: Proper handling of dynamic memory in class hierarchies
- Deep Copy: Creating independent copies of objects
Polymorphism allows objects of different classes to be treated as objects of a common base class, with the appropriate member functions being called based on the actual object type.
class Animal {
public:
virtual void makeSound() const {
std::cout << "Animal sound" << std::endl;
}
virtual ~Animal() {}
};
class Dog : public Animal {
public:
void makeSound() const override {
std::cout << "Woof!" << std::endl;
}
};
int main() {
Animal* animal = new Dog();
animal->makeSound(); // Outputs: Woof!
delete animal;
}
Virtual functions enable runtime polymorphism by allowing derived classes to override base class functions. The virtual
keyword indicates that a function can be overridden.
Key points:
- Virtual functions must be declared in the base class
- Derived classes use
override
keyword for clarity - Virtual destructors are crucial for proper cleanup
class Base {
public:
virtual void function() = 0; // Pure virtual function
virtual ~Base() {} // Virtual destructor
};
class Derived : public Base {
public:
void function() override {
// Implementation
}
};
Abstract classes are classes that have at least one pure virtual function. They cannot be instantiated and serve as interfaces that derived classes must implement.
class AAnimal {
protected:
std::string type;
public:
virtual void makeSound() const = 0; // Pure virtual function
virtual ~AAnimal() {}
std::string getType() const { return type; }
};
Proper memory management is crucial when dealing with inheritance and polymorphism:
- Virtual Destructors:
class Base {
public:
virtual ~Base() {} // Virtual destructor for proper cleanup
};
- Deep Copy:
class Dog : public Animal {
private:
Brain* brain;
public:
Dog(const Dog& other) : Animal(other) {
brain = new Brain(*other.brain); // Deep copy
}
Dog& operator=(const Dog& other) {
if (this != &other) {
Animal::operator=(other);
delete brain;
brain = new Brain(*other.brain);
}
return *this;
}
~Dog() {
delete brain;
}
};
The Brain class demonstrates proper resource management and deep copying:
class Brain {
private:
std::string ideas[100];
public:
Brain() {}
Brain(const Brain& other) {
for (int i = 0; i < 100; i++)
ideas[i] = other.ideas[i];
}
Brain& operator=(const Brain& other) {
if (this != &other) {
for (int i = 0; i < 100; i++)
ideas[i] = other.ideas[i];
}
return *this;
}
};
- Implement basic animal classes with polymorphic behavior
- Demonstrate the difference between virtual and non-virtual functions
- Show proper inheritance and method overriding
- Add a Brain class with dynamic memory allocation
- Implement deep copying for derived classes
- Handle memory management properly in constructors and destructors
- Convert Animal to an abstract class (AAnimal)
- Prevent instantiation of the base class
- Maintain functionality through derived classes
- Implement pure abstract classes as interfaces
- Create a material system with cloning capabilities
- Manage complex object relationships and memory