У меня есть абстрактный класс, пусть это будет например животное. Животное имеет чисто виртуальную функцию есть, что каждое животное должно реализовать, если они не хотят голодать. Я гарантирую, что таким образом может быть создан только ребенок Животного:
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()
{}
С этим, Кошка было бы:
Cat.hpp
class Cat : public Animal
{
public:
Cat();
void Eat() const;
};
Cat.cpp
Cat::Cat() : Animal()
{}
void Cat::Eat() const
{
// The cat eats.
}
Однако этот код не работает, он получает ошибку недопустимый абстрактный тип возвращаемого значения ‘Animal’ в GetAnimal, Поскольку Animal является абстрактным и не может быть создан, хотя мой API гарантирует, что это не так.
Какие умные решения могут иметь эту проблему? Я могу сделать функцию Есть не чистый и дать ему реализацию по умолчанию, но я бы не хотел этого делать.
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);
}
}
Вы говорите, что GetAnimal
должен вернуть Animal
объект, это не то, как наследование работает, наследование работает в основном через указатели. Когда вы пытаетесь вернуть объект типа, компилятор неявно должен создать Animal
объект, но это не разрешено, потому что Animal
это абстрактный класс. Даже если бы вы должны были сделать eat()
только virtual
у тебя все еще есть нарезка объектов проблема.
Вы можете сделать это вернуть Animal*
и освободите результат после того, как вы его использовали:
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);
}
}
Абонент:
Dog* myDog = GetAnimal(DOG);
//somewhere later when you dont need myDog anymore..(don't forget this!)
delete myDog;
Но если у вас есть доступ к C ++ 11 или более поздней версии, я рекомендую использовать умные указатели вместо необработанных указателей, чтобы указатель освободился сам по себе:
#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);
}
}
Абонент:
auto dog = GetAnimal(DOG);
//no explicit delete required.
Вы хотите, чтобы возвращаемый тип GetAnimal был указателем на Animal, а не на само Animal. В общем, всякий раз, когда вы пытаетесь использовать полиморфизм, указатели — лучший способ.
Мое лучшее предположение, что это то, что происходит: вы создаете экземпляр Cat. Затем в вашем операторе возврата, поскольку вы не возвращаете указатель, он должен сделать копию ваших данных. Поскольку тип возвращаемого значения — Animal, он пытается вызвать конструктор копирования по умолчанию для Animal. Поэтому он пытается создать экземпляр класса Animal, что, конечно, невозможно.
Виртуальные функции-члены работают с указателями. Просто измените немного свой API на что-то вроде этого:
std::unique_ptr<Animal> Animal::GetAnimal(const int animal)
{
switch(animal)
{
case CAT:
return std::make_unique<Cat>();
(...)
}
}
Обратите внимание, что я предполагаю, что вы используете, по крайней мере, C ++ 14