应用程序定义了3个要在插件中实现的接口。 Widget
始终是基础。
// Application code...
class Widget {
virtual void animate() = 0;
};
class BigWidget : public Widget {
};
class SmallWidget : public Widget {
};
每个接口实现都派生自NiceWidget
,它提供了一些插件内部公共信息。
// Plug-in code...
class NiceWidget {
// nice::Thing is only known in plug-in code.
nice::Thing thing();
};
class NiceBigWidget : public NiceWidget, public BigWidget {
void animate() override;
};
class NiceSmallWidget : public NiceWidget, public SmallWidget {
void animate() override;
};
从应用程序代码调用 func
。已知wid
由此插件实现。因此,wid也是NiceWidget
。 func
的目标是调用它的thing
方法。
// Plugin-in code...
void func(Widget* wid) {
// wid is either NiceBigWidget or NiceSmallWidget.
auto castedBig = dynamic_cast<NiceBigWidget*>(wid);
if (castedBig) {
castedBig->thing().foo();
return;
}
auto castedSmall = dynamic_cast<NiceSmallWidget*>(wid);
if (castedSmall) {
castedSmall->thing().foo();
return;
}
assert(false);
}
但是,随着层次结构大小的增加,尝试将wid
强制转换为每个Nice*
会变得非常糟糕。那里有更好的解决方案吗?
答案 0 :(得分:3)
首先:如果您知道wid
始终是NiceWidget*
,为什么不在func()
中说出来?你根本不需要演员:
void func(NiceWidget* wid)
{
wid->thing().foo(); // Done
}
即使您因任何原因无法更改功能签名,您也只需要一次演员:
void func(Widget* wid)
{
NiceWidget* casted = dynamic_cast<NiceWidget*>(wid);
if (casted)
casted->thing().foo();
else
throw std::exception(); // Well, throw the right exception
}
当然,如果你认为对你的目的更好,你可以assert()
而不是抛出异常。
在任何情况下,您只需要一个指向类的指针,该类定义您需要使用的函数(在本例中为thing()
),而不是大多数派生类。如果您将覆盖派生类中的函数,请将其设置为虚拟,并且无论如何都要完成。
答案 1 :(得分:0)
如果您知道每个NiceWidget
都是Widget
,则应考虑从NiceWidget
延长Widget
。
class Widget {
virtual void animate() = 0;
};
class BigWidget : public Widget {
};
class SmallWidget : public Widget {
};
class NiceWidget : Widget{
// nice::Thing is only known in plug-in code.
nice::Thing thing();
};
class NiceBigWidget : public NiceWidget, public BigWidget {
void animate() override;
};
class NiceSmallWidget : public NiceWidget, public SmallWidget {
void animate() override;
};
还会有另一个名为The diamond problem的问题,可以使用虚拟扩展来解决
之后,从dynamic_cast
到Widget
NiceWidget
应该没问题