dynamic_cast用于多个派生类

时间:2014-12-12 20:40:42

标签: c++ c++11 rtti

我有一个基类和 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)这样的东西。谢谢!

5 个答案:

答案 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 variantBoost any,这可能是您问题的替代选择。

答案 4 :(得分:1)

Stroustrup的“经典”型开关可能适合您的需求: https://parasol.tamu.edu/mach7/ https://parasol.tamu.edu/~yuriys/pm/

基本上它会让你像使用三种不同的实现之一那样基于obect类型做一个switch-case