#include <iostream>
#include <string>
#include <vector> // For std::vector
#include <memory> // For std::unique_ptr
// Base Class (from previous example)
class Animal {
public:
std::string name;
int age;
Animal(std::string n, int a) : name(n), age(a) {
std::cout << " Animal CTOR is called." << std::endl;
}
// Making 'eat' virtual allows derived classes to override it
// and ensures the correct derived version is called via base class pointer/reference.
virtual void eat() const {
std::cout << name << " is eating generic animal food." << std::endl;
}
void sleep() const { // Not virtual
std::cout << name << " is sleeping." << std::endl;
}
virtual ~Animal() {
std::cout << "Animal destructor called for " << name << std::endl;
}
};
// Derived Class: Dog (from previous example)
class Dog : public Animal {
public:
std::string breed;
Dog(std::string n, int a, std::string b) : Animal(n, a), breed(b) {
std::cout << " DOG CTOR is called." << std::endl;
}
// Override the virtual 'eat' method
void eat() const override {
std::cout << name << " the " << breed << " is happily eating dog food." << std::endl;
}
void bark() const {
std::cout << name << " says Woof! Woof!" << std::endl;
}
~Dog() override {
std::cout << "Dog destructor called for " << name << std::endl;
}
};
// Derived Class: Cat (from previous example)
class Cat : public Animal {
public:
Cat(std::string n, int a) : Animal(n, a) {
std::cout << " CAT CTOR is called." << std::endl;
}
void eat() const override { // Cat also overrides 'eat'
std::cout << name << " the Cat is delicately eating fish." << std::endl;
}
void meow() const {
std::cout << name << " says Meow!" << std::endl;
}
~Cat() override {
std::cout << "Cat destructor called for " << name << std::endl;
}
};
int main() {
// Using base class pointers to achieve polymorphism
std::vector<std::unique_ptr<Animal>> farm;
farm.push_back(std::make_unique<Dog>("Buddy", 3, "Golden Retriever"));
farm.push_back(std::make_unique<Cat>("Whiskers", 5));
farm.push_back(std::make_unique<Animal>("Generic Animal", 1)); // Can also add a base class object
// Iterate through the vector, treating all objects as 'Animal'
// The virtual 'eat()' method ensures the correct derived version is called at runtime.
for (const auto& animal_ptr : farm) {
animal_ptr->eat(); // Polymorphic call
animal_ptr->sleep(); // Non-polymorphic call (calls Animal's sleep())
std::cout << "---" << std::endl;
}
// Demonstrating non-polymorphic call (if 'eat' wasn't virtual)
Animal* a = new Dog("Fido", 2, "Labrador");
a->sleep(); // This calls Animal::sleep() because sleep() is not virtual
delete a; // Calls Animal destructor, then Dog destructor (because base destructor is virtual)
return 0;
}