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.
我有一个摘要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.