1

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.

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Santiago Gil
  • 1,292
  • 7
  • 21
  • 52

3 Answers3

5
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.
Community
  • 1
  • 1
Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
1

You want your return type of GetAnimal to be a pointer to an Animal rather than an Animal itself. In general, whenever you are trying to use polymorphism, pointers are the way to go.

My best guess is that this is what is happening: You are instantiating a Cat. Then in your return statement, since you aren't returning a pointer, it must make a copy of your data. Since the return type is Animal, it attempts to invoke the default Animal copy constructor. Therefore, it is trying to instantiate an Animal class, which of course cannot be done.

NateW
  • 908
  • 1
  • 8
  • 28
0

Virtual member functions works with pointers. Just change a bit your API, to something like that:

std::unique_ptr<Animal> Animal::GetAnimal(const int animal)
{
    switch(animal)
    {
    case CAT:
        return std::make_unique<Cat>();
    (...)
    }
}

Note that I'm assuming you are using, at least, C++14

Amadeus
  • 10,199
  • 3
  • 25
  • 31
  • @curiousguy dynamic inherintance, that is implemented with virtual functions works with pointer, exactly as OP ask – Amadeus Dec 28 '16 at 22:16
  • Virtual functions work normally on an automatic object; of course, the dynamic type is known at compiler, but they "work". If you need to choose the type of an object at runtime, of course you need dynamic allocation. – curiousguy Dec 28 '16 at 23:02
  • I understand fully well the OP, and the answer. Virtual functions are *not* the problem here. When you return by value, you create another instance. This is the issue: that instance has different type. And here, it's an abstract class so you can't even create it. The problem is the creation of a distinct instance of a distinct type. – curiousguy Dec 29 '16 at 02:32
  • @curiousguy What is the purpose of virtual function if not to access derived function from its base class? Virtual functions has runtime overhead, so you only declare a function virtual if you want to access it via its base class, and this access is done via pointers (runtime polymorphism). Can you declare all functions virtual? Yes, you can, but should you? So, what is wrong with the answer? It creates a derived object that can be accessed via base object. That the purpose of virtual functions. Thats how you use virtual functions. Via pointers. – Amadeus Dec 29 '16 at 02:53
  • A virtual call is dispatched based on the dynamic type; the purpose of virtual function is the support of virtual dispatch. Virtual function calls only have overhead when the dynamic type is not known at compile time; for automatic objects, there is no overhead. You probably should not declare all functions virtual; you should not declare any virtual function if you have no specified the requirements on the overrider, something which most people don't pay attention to. – curiousguy Dec 29 '16 at 03:08
  • Congrats, you are saying **almost** what I've said before, just in other words. But, you need to read more about [virtual functions overhead](http://stackoverflow.com/questions/667634/what-is-the-performance-cost-of-having-a-virtual-method-in-a-c-class). To me, this is the end of this discussion. – Amadeus Dec 29 '16 at 10:43
  • "_Congrats, you are saying almost what I've said before_" No "_Can you declare all functions virtual? Yes, you can"_ No, you cannot. Non trivial program will fail to compile or compile but fail to work properly. – curiousguy Dec 30 '16 at 04:12