Search code examples
c++polymorphismdowncast

Upcasting/Downcasting to handle mutliple derived classes by base class


Let's say I have a base class and multiple derived classes and I want to avoid having multiple variables/functions for every derived class type.

The only way I found to realize this is using up-/downcasting. But especially downcasting is often considered bad practice - so whats the way to go?

Example:

#include <stdio.h>
#include <stdlib.h>
#include <iostream>

class Animal{
protected:
    Animal(std::string val) { name = val; }
public:
    virtual ~Animal() = default;
    std::string name;
};

class Cat: public Animal {
public:
    Cat(std::string name) : Animal(name) { }
    ~Cat() = default;

    void purr();
};

class Dog: public Animal {
public:
    Dog(std::string name): Animal(name) { }
    ~Dog() = default;

    void bark();
};

void pet(Animal *animal) {
    if (!animal)
        return;

    if (dynamic_cast<Cat*>(animal))
        std::cout << "petting my cat " << animal->name << std::endl;
    else if (dynamic_cast<Dog*>(animal))
        std::cout << "petting my dog " << animal->name << std::endl;
};

int main()
{
    Animal *currentPet = new Cat("Lucy");
    pet(currentPet);
    
    // after many joyful years - RIP
    delete currentPet;
    currentPet = new Dog("Diego");
    
    pet(currentPet);

    delete currentPet;
    currentPet = NULL;

    return 0;
}

Is this totally fine or still considered bad design/practice?


Solution

  • I feel like the best way to achieve this would be having a virtual method petted() in base class and override it in every subclass to suit the needs of that subclass.

    class Animal { 
    // ...
    virtual void petted() { std::cout << "Petted general animal"; }
    }
    class Dog: public Animal { 
    // ...
    void petted() { std::cout << "Petted dog [...]"; }
    }
    class Cat: public Animal {
    // ...
    void petted() { std::cout << "Petted cat [...]"; } 
    }
    // this doesn't even have to be a separate function...
    void pet(Animal* a) {
     a->petted();
    }
    

    Making it virtual will let you achieve runtime polymorphism through base-class pointer (actual type of the instance the pointer is pointing to is checked at the runtime), which will choose the proper "version" of the petted() method.

    At least that's what I would do (I've only written in C++ for a year and a half so far).