Last active
February 28, 2023 15:55
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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