C ++使用纯虚函数实例化抽象类的子级

时间:2016-11-09 18:47:55

标签: c++ polymorphism abstract-class return-by-value

我有一个抽象类,例如 Animal 。动物有一个纯粹的虚拟功能 eat ,如果他们不想饿死,每个动物都必须实施。我确保只有动物的孩子才能通过这种方式实例化:

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 将是:

Cat.hpp

class Cat : public Animal
{

public:

    Cat();

    void Eat() const;
};

Cat.cpp

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

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

但是这段代码不起作用,它会在 GetAnimal 中出现无效的抽象返回类型&#39; Animal&#39; ,因为Animal是抽象的并且无法实例化,尽管我的API确保它不会。

这个问题有哪些智能解决方案?我可以做 Eat 这个函数并不是纯粹的,并给它一个默认的实现,但我不想这样做。

3 个答案:

答案 0 :(得分:3)

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,您仍然会遇到object slicing问题。

您可以让它返回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.

答案 1 :(得分:1)

您希望GetAnimal的返回类型是指向Animal而不是Animal本身的指针。通常,无论何时尝试使用多态,都可以使用指针。

我最好的猜测是,这就是正在发生的事情:你正在实例化一只猫。然后在return语句中,由于您没有返回指针,因此它必须复制您的数据。由于返回类型是Animal,它会尝试调用默认的Animal复制构造函数。因此,它试图实例化一个Animal类,当然这是无法完成的。

答案 2 :(得分:0)

虚拟成员函数与指针一起使用。只需将您的API更改为类似的内容:

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

请注意,我假设您正在使用,至少是C ++ 14