我有一个名为Animal的课程
class Animal
{
std::string name;
public:
Animal(string n)
{
name = n;
}
string getname()
{
return name;
}
};
和两个继承的类Cat和Dog
class Cat : public Animal
{
public:
Cat(string name):Animal(name)
{}
};
class Dog : public Animal
{
public:
Dog(string name):Animal(name){}
};
我有一个名为AnimalQueue的类,它包含2个列表,猫列表和狗列表
class AnimalQueue
{
std::list<Cat*> cats;
std::list<Dog*> dogs;
public:
void Enqueue(Animal a)
{
if(a.getname().compare("Cat") == 0)
{
// what should i do here
}
else
{
// what should i do here
}
}
};
我想要它,当我进入猫然后它应该进入猫列表和狗也在Enqueue功能相同。我不确定它是如何工作的,我如何输入从动物到猫或狗的演员表。 我试过了
Cat *c = (Cat*) &a;
但它不起作用。
int main()
{
string name = "Cat";
Cat c(name);
name = "Dog";
Dog d(name);
AnimalQueue aq;
aq.Enqueue(c);
aq.Enqueue(d);
}
这是工作代码,您可以在编辑器中复制粘贴。你可以改变Animal的签名或者你想要的任何东西,这样可以帮助我清楚地了解继承中的类型转换。
答案 0 :(得分:4)
供参考,以下是此代码在C ++中的外观:
#include <string>
#include <utility>
#include <vector>
class Animal
{
std::string name_;
public:
explicit Animal(std::string name) : name_(std::move(name)) {}
virtual ~Animal() {}
};
class Cat : public Animal { using Animal::Animal; };
class Dog : public Animal { using Animal::Animal; };
class AnimalQueue
{
std::vector<Cat> cats_;
std::vector<Dog> dogs_;
public:
void Enqueue(Animal const & animal)
{
if (Cat const * p = dynamic_cast<Cat const *>(&animal))
{
cats_.push_back(*p);
}
else if (Dog const * p = dynamic_cast<Dog const *>(&animal))
{
dogs_.push_back(*p);
}
else
{
// discarding unrecognized animal
}
}
};
用法:
AnimalQueue q;
q.Enqueue(Dog("dog"));
q.Enqueue(Cat("cat"));
备注:强>
C ++基础知识:
using namespace std
。std::vector
,而不是std::list
。explicit
。多形性:
高级设计:
Cat
和Dog
)会更合适。std::unique_ptr<Animal>
,并且必须调整动态强制转换。答案 1 :(得分:1)
在您的代码中有两个相当明显的问题:
动态绑定是指在运行时将名称绑定到函数而不是编译时。如果您正在处理运行时polymorphism,则使用动态绑定允许您使用对象的动态类型调用函数(Dog
)而不是静态类型(Animal
)。这很有用,因为当派生类覆盖继承的基类函数时,最好调用派生类函数而不是基类1。
要在C ++中实现动态绑定,您需要合并虚拟功能。如果使用虚函数,编译器将延迟评估函数的名称,直到运行时可以使用对象的虚拟表 1 将名称绑定到正确函数的地址。
要提供getname()
动态绑定,请使用virtual
说明符声明它:
virtual string getname();
如果派生类重写此函数,那将是被调用的函数,否则将调用基类的版本。可以将此虚函数视为默认实现。
复制派生类实例的基础部分时会发生切片。例如,当您按值获取基类参数并传入派生类时,可能会发生这种情况 - 派生的部分将被&#34;切片&#34;关闭。当发生这种情况时,您只能使用对象的静态部分来调用函数和访问数据 - 因此,为什么在getname()
中调用Enqueue()
始终会调用Animal::getname()
。
要防止这种情况,您必须使用引用或指向基类的指针。这可以防止切片,因为传递引用或指针时不会发生复制。
让Enqueue
对Animal
实例进行左值引用:
void Enqueue(Animal const& a);
重要:如果您使用基类指向派生实例的指针,为了防止未定义的行为,您必须提供基础类一个虚拟析构函数。这样做允许派生类&#39;析构函数与基类一起调用&#39;析构函数。
这只是对您的问题的解释,您可以参考@Kerrek SB的答案获得一个好的解决方案。
1 虚拟表是一种非标准实现,它实际上取决于您的系统如何解析虚函数名称。