有没有办法推断出一个物体的类型?

时间:2012-12-27 14:55:09

标签: c++ dispatch

这可能是一个愚蠢的问题,我怀疑我知道答案(不)因为我好像在这里撞墙。

鉴于我有一组派生自某个类的对象:

class BaseClass;
class DerivedA: public BaseClass;
class DerivedB: public BaseClass;
class DerivedC: public BaseClass;
std::vector<BaseClass> myCollection;

我想根据特定类的类型调用方法:

class Processor {
  void doSomething(DerivedA a, DerivedB b);
  void doSomething(DerivedA a, DerivedC c);
}

问题是,如果我访问集合中的各个项目并尝试在“处理器”中调用“doSomething”方法,它将无法确定使用哪种方法(afaik)。所以我的问题是:有没有办法用正确的派生类型获取集合中的项目?

2 个答案:

答案 0 :(得分:2)

如果要保持doSomething方法不变,这就是所谓的multiple dispatch,目前C ++不支持。

如果它是BaseClass的虚拟成员函数,那么它就是在它被调用的对象上运行C ++多态,但它仍然不会自动推断出争论的类型。

要解决此问题,您可以执行类似于早期链接

中建议的操作
void collideWith(Thing& other) {
     // dynamic_cast to a pointer type returns NULL if the cast fails
     // (dynamic_cast to a reference type would throw an exception on failure)
     if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
         // handle Asteroid-Asteroid collision
     } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
         // handle Asteroid-Spaceship collision
     } else {
         // default collision handling here
     }
 }

基本上继续强制转换为各种可能的派生类,直到一个人工作并适当地调用其中一个方法(因为编译器知道你试图转换为什么类型,所以没有特别的努力)。

重要提示:正如@WhozCraig所指出的那样,你的向量需要保留指针以避免Object-Slicing,并使整个问题无法实现。

答案 1 :(得分:1)

好的,是的,您应该使用多态性,如上所述。如果你的函数需要处理2个对象,尽管它变得非常复杂。

如果派生形成有限的集合并相互了解,则可以使用双重调度。它并不完美,但它解决了这个特殊情况。

class DerivedA;
class DerivedB;
class DerivedC;

class BaseClass
{
 public:
     virtual ~BaseClass();

     virtual void doSomethingWithBase( BaseClass & b2 ) = 0;
     virtual void doSomethingWithDerivedA( DerivedA & da ) = 0;
     virtual void doSomethingWithDerivedB( DerivedB & db ) = 0;
     virtual void doSomethingWithDerivedC( DerivedC & dc ) = 0;
};

class DerivedA : public BaseClass
{
   public:

      void doSomethingWithBase( BaseClass & b2 )
      {
           b2.doSomethingWithDerivedA( *this );
      }

      void doSomethingWithDerivedA( DerivedA & da )
      {
           // implement for two DerivedA objects
      }

      void doSomethingWithDerivedB( DerivedB & db )
      {
           // implement for an A and B
      }

      void doSomethingWithDerivedC( DerivedC & dc )
      {
          // implement for an A and C
      }
 };

 // implement DerivedB to call doSomethingWithDerivedB on its parameter
 // implement DerivedC to call doSomethingWithDerivedC on its parameter.

你明白了。从你打电话的地方你不需要知道你有哪两种类型,你永远不需要实际查看它。但是,如果您添加更多实现,则需要编辑大量代码,并且可能会考虑使用某种查找表。

如果您需要一个类来定义自己,您可以使用某种虚拟ID。

  class BaseClass
  {
      public:
         virtual int id() const = 0;
  };

然后你得到类来揭示它们的id并根据这些将处理这两个对象的id找到表中的处理程序。 id不必是整数,它们可以是字符串,这使得更容易避免命名冲突,这比基类的双分派方法不知道它的派生类或它们彼此了解更有优势,可扩展的。你也没必要处理每一对。