c++ - 如果没有 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 将是一个很大的好处。
解决方案
一种方法是从主界面显式公开界面转换函数:
#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();
推荐阅读
- android - 失败:忍者:'vendor/google/taimen.... 构建 lineageOS 17.1 时
- javascript - 如何在 React JS 中单击按钮时将数据传递给独立组件?
- java - 断言 OutputStreamWriter 包含正确的 OutputStream 实例
- python - 使用 glom 将字典转换为字符串
- highcharts - 如何删除 x 和 y 轴上的 Highcharts 网格结束线?
- java - Okhttp3 jar 缺少 okio?
- reactjs - Expo Android Sign in Error keytool 错误:java.lang.Exception:Alias <> 不存在
- sparql - 耶拿数据集更新
- angular - Angular -> 将 Cookie 中的用户名放入输入字段
- javascript - Vue - 动态组件事件监听器