Santiago Gil Santiago Gil - 1 month ago 11
C++ Question

C++ Instantiating childs of an abstract class with pure virtual functions

I have an abstract class, let it be for example Animal. An Animal has a pure virtual function eat, which every animal must implement if they don't want to starve. I ensure that only a child of an Animal can be instantiated by this way:

Animal.hpp

class Animal
{

public:

enum eAnimal{
CAT=0,
DOG=1,
BIRD=2
};

// Instantiates the desired animal.
static Animal GetAnimal(const int animal);

virtual void Eat() const = 0;

protected:

Animal();
};


Animal.cpp

Animal Animal::GetAnimal(const int animal)
{
switch(animal)
{
case CAT:
return Cat();
case DOG:
return Dog();
case BIRD:
return Bird();
default:
cerr << "Animal not recognized." << endl;
exit(-1);
}
}

Animal::Animal()
{}


With this, a Cat would be:

Cat.hpp

class Cat : public Animal
{

public:

Cat();

void Eat() const;
};


Cat.cpp

Cat::Cat() : Animal()
{}

void Cat::Eat() const
{
// The cat eats.
}


However this code doesn't work, it gets an error invalid abstract return type 'Animal' in GetAnimal, because Animal is abstract and can't be instantiated, altough my API ensures it won't.

Which smart solutions may this problem have? I can do the function Eat not pure and give it a default implementation, but I'd like not to do it.

Answer
Animal Animal::GetAnimal(const int animal)
{
    switch(animal)
    {
    case CAT:
        return Cat();
    case DOG:
        return Dog();
    case BIRD:
        return Bird();
    default:
        cerr << "Animal not recognized." << endl;
        exit(-1);
    }
}

You say that GetAnimal should return an Animal object, this is not how inheritance works, inheritance works mainly through pointers. When you try to return an object of a type the compiler implicitly has to create an Animal object, but it's not allowed to because Animal is an abstract class. Even if you were to make eat() only virtual you'd still have the object slicing problem.

You can make it return a Animal* and free the result after you used it:

Animal* Animal::GetAnimal(const int animal)
{
    switch(animal)
    {
    case CAT:
        return new Cat();
    case DOG:
        return new Dog();
    case BIRD:
        return new Bird();
    default:
        cerr << "Animal not recognized." << endl;
        exit(-1);
    }
}

Caller:

Dog* myDog = GetAnimal(DOG);

//somewhere later when you dont need myDog anymore..(don't forget this!)
delete myDog;

But if you have access to C++11 or later I recommend using smart pointers instead of raw pointers to let the pointer free itself:

#include <memory>
std::unique_ptr<Animal> Animal::GetAnimal(const int animal)
{
    switch(animal)
    {
    case CAT:
        return std::make_unique<Cat>();
    case DOG:
        return std::make_unique<Dog>();
    case BIRD:
        return std::make_unique<Bird>();
    default:
        cerr << "Animal not recognized." << endl;
        exit(-1);
    }
}

Caller:

auto dog = GetAnimal(DOG);
//no explicit delete required.