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

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

本文关键字:抽象类 子类 实例化 函数 虚拟 C++      更新时间:2023-10-16

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

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中得到一个错误无效的抽象返回类型"Animal">,因为Animal是抽象的,不能实例化,尽管我的API确保它不会。

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

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本身的指针。一般来说,无论何时尝试使用多态性,指针都是首选。

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

虚拟成员函数使用指针。只要把你的API改一点就可以了:

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

注意,我假设您至少使用C++14