我有一个基类和 n 派生类。我想实例化一个派生类并将其发送到一个接收基类作为参数的函数。在函数内部,我通过使用dynamic_cast找到了哪种类型的派生类,但我不想使用几个if-else语句。相反,我想知道是否有办法找出哪个派生类是为了投射它。 在这里,我以我的代码为例。
class animal{
public:
virtual ~animal() {}
int eyes;
};
class dog: public animal{
public:
int legs;
int tail;
};
class fish: public animal{
public:
int mostage;
};
void functionTest(animal* a){
if(dynamic_cast<fish*>(a) != NULL){
do_something();
}
else if(dynamic_cast<dog*>(a) != NULL){
do_something();
}
};
我想对此采取更一般的方法。像dynamic_cast(a)这样的东西。谢谢!
答案 0 :(得分:7)
如果您需要在几分钟内展示一些内容,那么快速草稿就可以了,但通常您会尝试避免以这种方式使用dynamic_cast - 如果在错误的地方使用它会导致极高的维护成本。可以使用各种模式,例如简单方法重载,Visitor模式或虚拟“GetType”函数(如果您喜欢模式,可以使用curiously recurring template pattern实现)。
我将列出所有3种方法。第一个是迄今为止最直接,最容易使用的。另外两个方面的优点是,每个方法都将决定做什么做到了代码的不同部分,这可能是一个巨大的好处(或缺点)。
让我们假设这是你想要做的:
void functionTest(animal* a)
{
if(dynamic_cast<fish*>(a) != NULL)
blub();
else if(dynamic_cast<dog*>(a) != NULL)
bark();
};
简单的虚拟功能方法:
class animal {
public:
virtual ~animal() {}
virtual void do_something() = 0;
int eyes;
};
class dog : public animal {
public:
virtual void do_something() { bark(); } // use override in C++11
int legs;
int tail;
};
class fish: public animal {
public:
virtual void do_something() { blub(); } // use override in C++11
int mostage;
};
void functionTest(animal* a)
{
if (a) a->do_something();
};
访客方式:
class IVisitor {
public:
~IVisitor(){}
virtual void visit(const fish&){}
virtual void visit(const dog&){}
virtual void visit(const animal&){}
};
class animal {
public:
virtual ~animal() {}
virtual void accept(IVisitor& visitor) = 0;
int eyes;
};
class dog : public animal {
public:
virtual void accept(IVisitor& visitor) { visitor.visit(*this); } // use override in C++11
int legs;
int tail;
};
class fish : public animal {
public:
virtual void accept(IVisitor& visitor) { visitor.visit(*this); } // use override in C++11
int mostage;
};
class MyVisitor : public IVisitor {
public:
virtual void visit(const fish&) { blub(); } // use override in C++11
virtual void visit(const dog&) { bark(); } // use override in C++11
};
void functionTest(animal* a)
{
if (a)
{
MyVisitor v;
a->accept(v);
}
};
GetType方法,CRTP spice:
class animal {
public:
virtual ~animal() {}
virtual const type_info& getType() const = 0; // careful. typeinfo is tricky of shared libs or dlls are involved
int eyes;
};
template <class T>
class BaseAnimal : public animal {
// these are C++11 features. Alternatives exist to ensure T derives from BaseAnimal.
static_assert(std::is_base_of<BaseAnimal,T>(,"Class not deriving from BaseAnimal");// C++11
virtual const type_info& getType() const { return typeid(T); }
};
class dog : public BaseAnimal<dog> {
public:
int legs;
int tail;
};
class fish : public BaseAnimal<fish> {
public:
int mostage;
};
void functionTest(animal* a)
{
if (!a)
return;
if (a->getType() == typeid(fish))
blub();
else if (a->getType() == typeid(dog))
bark();
};
请注意,您应该将上述示例视为伪代码。要获得最佳实践,您需要查找模式。此外,奇怪的重复模板模式也可用于第二种方法,或者可以很容易地从第三种方法中删除。在这些情况下,这只是为了方便。
答案 1 :(得分:3)
您可以使用虚拟功能:
class animal{
public:
virtual ~animal() {}
virtual void do_thing() = 0;
};
class dog: public animal{
public:
void do_thing() override { std::cout << "I'm a dog" << std::endl; }
};
class fish: public animal{
public:
void do_thing() override { std::cout << "I'm a fish" << std::endl; }
};
然后
void functionTest(animal& a){
a.do_thing();
}
作为替代方案,如果您想避免使用许多虚拟功能,可以使用visitor pattern
答案 2 :(得分:3)
让我强烈建议你不要在这里做你所拥有的,并遵循每个人都使用多态性(例如虚函数)的非常明智的建议。您已经概述的方法可以起作用,但它正在反对语言提供的工具。你要做的就是语言具有虚函数的原因。
使用您的方法,如果您添加一个新的animal
子类,那么您还必须更改function_test()
,并且function_test()
正在执行编译器为虚拟函数执行的操作,但是以一种更加笨拙和低效的方式。
使用虚函数,您所要做的就是在新的子类中实现do_something()
,编译器负责其余的工作。
不要使用dynamic_cast<>()
。这不是它的用途。
答案 3 :(得分:2)
考虑将此“开关”实现为虚拟功能。
如果您不想这样,可以使用示例中的dynamic_cast
,或者使用typeid运算符计算typeid
结果到函数的映射实现do_something
代码。
但是,我不建议这样做,因为你最终得到一个手写的vtable。最好使用虚函数,让编译器生成映射。
如需更多阅读,我推荐Herb Sutter的文章Type inference vs static/dynamic typing。他提到Boost variant和Boost any,这可能是您问题的替代选择。
答案 4 :(得分:1)
Stroustrup的“经典”型开关可能适合您的需求: https://parasol.tamu.edu/mach7/ https://parasol.tamu.edu/~yuriys/pm/
基本上它会让你像使用三种不同的实现之一那样基于obect类型做一个switch-case