dynamic_cast多个派生类

dynamic_cast for multiple derived classes

本文关键字:派生 cast dynamic      更新时间:2023-10-16

我有一个基类和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(。谢谢!

如果您需要在几分钟内演示某些内容,那么为快速草稿执行此操作会很棒,但通常您会尽量避免以这种方式使用dynamic_cast - 如果在错误的地方使用,可能会导致极高的维护成本。可以使用各种模式,例如简单的方法重载、访问者模式或虚拟的"GetType"函数(如果您喜欢模式,可以使用奇怪的重复模板模式来实现(。

我将列出所有 3 种方法。第一个是迄今为止最直接,最容易使用的。其他 2 个的优点是,它们中的每一个都将要做什么的决定转移到代码的不同部分,这可能是一个巨大的好处(或缺点(。

让我们假设这是您想要做的:

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香料:

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();
};

请注意,您应该将上述示例视为伪代码。对于最佳实践,您需要查找模式。此外,奇怪的重复模板模式也可以在第二种方法中使用,或者可以轻松地从第三种方法中删除。在这些情况下,这只是为了方便。

您可以使用虚函数来实现:

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();
}

作为替代方案,如果您想避免拥有许多虚拟功能,您可以使用访客模式

让我强烈敦促你不要做你在这里做的事情,并遵循每个人都给出的非常明智的建议来使用多态性(例如虚函数(。您概述的方法可以工作,但它与语言提供的工具背道而驰。你试图做的正是为什么语言具有虚拟功能。

使用你的方法,如果你添加一个新的animal子类,那么你还必须更改function_test()function_test()正在做编译器无论如何都会为虚函数做的事情,但以一种更笨拙和低效的方式。

使用虚函数,您所要做的就是在新子类中实现do_something(),编译器负责其余的工作。

不要为此使用dynamic_cast<>()。这不是它的用途。

考虑将此"开关"实现为虚拟函数。

如果不希望这样做,可以使用示例中的dynamic_cast,也可以使用 typeid 运算符计算typeid到实现do_something代码的函数的结果的映射。

但是,我不建议这样做,因为您最终只会得到一个手动编码的 vtable。最好使用虚函数并让编译器生成映射。

对于其他阅读,我推荐Herb Sutter的文章类型推断与静态/动态类型。他提到了Boost变体和Boost任何变体,这可能是您问题的可能替代方案。

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

基本上,它将允许您使用三种不同的实现之一来执行基于obect类型的开关案例。