C++ 使用纯虚函数实例化抽象 class 的子项

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

我有一个摘要class,比如说动物。 Animal 有一个纯虚函数 eat,每个动物如果不想挨饿就必须实现它。我确保只有 child 的 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.
}

但是这段代码不起作用,它得到一个错误 invalid abstract return type 'Animal' in GetAnimal,因为 Animal 是抽象的,无法实例化,尽管我的 API 确保它不会。

这个问题可能有哪些聪明的解决办法?我可以做函数 Eat not pure 并给它一个默认实现,但我不想这样做。

您希望 return 类型的 GetAnimal 成为指向 Animal 而不是 Animal 本身的指针。通常,无论何时您尝试使用多态性,指针都是最佳选择。

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

虚拟成员函数使用指针。只需将您的 API 稍作更改即可:

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

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

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 应该 return 一个 Animal 对象 ,这不是继承的工作方式,继承主要通过指针工作。当您尝试 return 一个类型的对象时,编译器隐式必须创建一个 Animal 对象,但不允许这样做,因为 Animal 是一个抽象 class。即使您只制作 eat() virtual,您仍然会遇到 object slicing 问题。

您可以将其设为 return 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.