首页 > 解决方案 > 如果没有 RTTI,在 C++ 中,我如何在运行时确定集合中的对象是否实现了接口

问题描述

我有一组共享公共基类的对象,但其中一些还实现了一个或多个接口。在迭代它们时,我希望只有当对象属于实现它们的类时才能运行这些接口的方法。

假设我有这样的事情:

include <iostream>
#include <vector>

using namespace std;

class IMovable
{
public:
  virtual bool canFly ()
  {
    return false;
  }

  virtual bool canSwim ()
  {
    return false;
  }

  virtual bool canCrawl ()
  {
    return false;
  }
};

class IFlying: virtual public IMovable
{
public:
  virtual void fly () = 0;
};

class IAquatic: virtual public IMovable
{
public:
  virtual void swim () = 0;
};

class ITerrestrial: virtual public IMovable
{
public:
  virtual void crawl () = 0;
};

class Animal
{
public:
  void eat ()
  {
    cout << "Yummy";
  }
};

class Bird:public Animal, public IFlying
{
public:
  void fly () override
  {
    cout << "Fly like a bird";
  }
  bool canFly () override
  {
    return true;
  }
};

class Frog:public Animal, public IAquatic, public ITerrestrial
{
public:
  void swim () override
  {
    cout << "Swimming frog";
  }
  void crawl () override
  {
    cout << "Ribbit";
  }
  bool canSwim () override
  {
    return true;
  }
  bool canCrawl () override
  {
    return true;
  }
};


main ()
{
  std::vector < IMovable * >animals;

  Frog f;
  Bird b;

  animals.push_back (&f);
  animals.push_back (&b);
  for (auto animal:animals)
  {
    if (animal->canCrawl ())
    {
      static_cast < ITerrestrial * >(animal)->crawl ();
    }

    if (animal->canSwim ())
    {
      static_cast < IAquatic * >(animal)->swim ();
    }

    if (animal->canFly ())
    {
      static_cast < IFlying * >(animal)->fly ();

    }
      return 0;
  }
}

这不起作用,因为我有虚拟继承,但是如果我没有它,则基数是不明确的,并且如果我没有将向量设置为接口的基本类型,我就不能向下转换。不幸的是,我不能使用 RTTI,所以这是不可能的。

我想知道是否有一种不那么复杂的方法来实现这个结果。不必做一堆 static_casts 将是一个很大的好处。

标签: c++polymorphism

解决方案


一种方法是从主界面显式公开界面转换函数:

#include <vector>
#include <iostream>

struct IFlying;
struct IAquatic;
struct ITerrestrial;

struct IMovable {
    virtual ~IMovable() noexcept = default;
    virtual IFlying* canFly() noexcept { return 0; }
    virtual IAquatic* canSwim() noexcept { return 0; }
    virtual ITerrestrial* canCrawl() noexcept { return 0; }
};

struct IFlying : virtual IMovable {
    virtual void fly() = 0;
};

struct IAquatic : virtual IMovable {
    virtual void swim() = 0;
};

struct ITerrestrial : virtual IMovable {
    virtual void crawl() = 0;
};

struct Bird : IFlying {
    void fly() override { std::cout << "Fly like a bird\n"; }
    IFlying* canFly() noexcept override { return this; }
};

struct Frog : IAquatic, ITerrestrial {
    void swim() override { std::cout << "Swimming frog\n"; }
    void crawl() override { std::cout << "Ribbit\n"; }
    IAquatic* canSwim() noexcept override { return this; }
    ITerrestrial* canCrawl() noexcept override { return this; }
};

int main() {
    std::vector<IMovable*> animals;
    Frog f;
    Bird b;
    animals.push_back(&f);
    animals.push_back(&b);
    for(auto animal : animals) {
        if(ITerrestrial* t = animal->canCrawl())
            t->crawl();
        if(IAquatic* t = animal->canSwim())
            t->swim();
        if(IFlying* t = animal->canFly())
            t->fly();
    }
}

这种机制比dynamic_cast. dynamic_cast在继承层次结构中追逐type_info基类的指针,试图找到所需的基类,而这些转换需要一次虚拟调用。


而不是命名的转换函数,如:

virtual IFlying* canFly() noexcept { return 0; }

它们也可以是隐式转换运算符:

virtual operator IFlying*() noexcept { return 0; }

接着:

if(IFlying* t = *animal)
    t->fly();

推荐阅读