Skip to content

Instantly share code, notes, and snippets.

@stungeye
Last active February 28, 2023 15:55
Show Gist options
  • Save stungeye/3eb13795c733a5bfaddbc7794fb1c271 to your computer and use it in GitHub Desktop.
Save stungeye/3eb13795c733a5bfaddbc7794fb1c271 to your computer and use it in GitHub Desktop.
Base and Derived Types in C++ with Polymorphic Functions and Collections using Pointers
#include <iostream> // std::cout
#include <string> // std::string
#include <vector> // std::vector
#include <memory> // std::unique_ptr, std::make_unique
class Animal {
// Base Class - Eventually Abstract
std::string name;
protected:
Animal(const std::string& name) : name{name} {
std::cout << "Animal Constructor\n";
}
public:
virtual ~Animal() = default; // Best Practice: Abstract classes should have virtual destructors.
std::string getName() const {
return name;
}
// Pure Virtual Function Makes Class Abstract.
virtual std::string speak() const = 0;
};
class Lion : public Animal {
public:
Lion(const std::string& name) : Animal{name} {
std::cout << "Lion Constructor\n";
}
std::string speak() const override {
return "Roar!";
}
void combMane() const {
std::cout << "Combing Mane!\n";
}
};
class Wolf : public Animal {
public:
Wolf(const std::string& name) : Animal{name} {
std::cout << "Wolf Constructor\n";
}
std::string speak() const override {
return "Howl!";
}
};
// Polymorphic Function.
void animalSpeaker(const Animal& animal) {
std::cout << animal.getName() << " : " << animal.speak() << "\n";
}
int main() {
// Creating regular old objects of our derived types.
Lion lion{"Fifi"};
Wolf wolf{"George"};
std::cout << lion.getName() << " : " << lion.speak() << "\n";
std::cout << wolf.getName() << " : " << wolf.speak() << "\n";
// An Animal reference can reference a derived class but....
Animal& animalRef{lion};
std::cout << animalRef.getName() << " : " << animalRef.speak() << "\n";
// DANGER! Object Slicing! Creates a "Frankenobject"!
// animalRef = wolf;
// std::cout << animalRef.getName() << " : " << animalRef.speak() << "\n";
// See bottom of page here: https://www.learncpp.com/cpp-tutorial/object-slicing/
// Pointers to Lion and Wolf objects.
std::cout << "\nLion by way of animal pointer: \n";
Animal* animalPointer{&lion};
std::cout << animalPointer->getName() << " : " << animalPointer->speak() << "\n";
std::cout << "\nWolf by way of animal pointer: \n";
animalPointer = &wolf;
std::cout << animalPointer->getName() << " : " << animalPointer->speak() << "\n";
// Vectors of derived type objects. No problem.
std::cout << "\nVector of Lions: \n";
const std::vector<Lion> lions{{"Leo"}, {"Laura"}, {"Lambert"}};
for (const auto& lion : lions) {
animalSpeaker(lion);
}
// Vectors of Animal pointers to derived type objects.
std::cout << "\nVector of animal pointers to lions and wolves:\n";
const std::vector<Animal*> animals{new Lion{"Leo"}, new Wolf{"Wanda"}};
for (const auto& animal : animals) {
const auto lionPtr{dynamic_cast<Lion*>(animal)};
if (lionPtr) {
lionPtr->combMane();
}
animalSpeaker(*animal);
}
// Free up the heap allocated memory.
// Not really needed as program is ending, but good practice.
for (auto animal : animals) {
delete animal;
animal = nullptr;
}
std::cout << "\nVector of animal smart pointers to lions and wolves:\n";
std::vector<std::unique_ptr<Animal>> smartAnimals;
// Can't place the lion and wolf directly into the vector using an initializer list.
// This is because the values created by make_unique cannot be moved into the vector.
// So we must push_back or emplace_back the derived type objects:
smartAnimals.push_back(std::make_unique<Lion>("Leo"));
smartAnimals.push_back(std::make_unique<Wolf>("Wanda"));
for (auto& animal : smartAnimals) {
const auto lionPtr{dynamic_cast<const Lion*>(animal.get())};
if (lionPtr) {
lionPtr->combMane();
}
animalSpeaker(*animal);
}
// No need to delete the memory used by the smart pointers!
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment