避免dynamic_cast而不增加耦合

时间:2019-07-03 16:25:46

标签: c++

假设我想避免以下代码中的dynamic_cast

struct Base { ... stuff ... };
struct HasColor {
  virtual Color getColor() const = 0;
  virtual void setColor(Color) = 0;
};
struct Foo : public Base, public HasColor {
  ... implements HasColor methods
};

...

vector<Base*> collection;
...
for(auto element : collection) {
   if(auto hasColor = dynamic_cast<HasColor*>(element)) {
      hasColor->SetColor(myColor);
   }
}

一种解决方案是添加一个向下转换的方法:

struct Base {
  HasColor* toHasColor() { return nullptr; }
};
struct Foo : public Base, public HasColor {
  ...
  HasColor* toHasColor() { return this; }
};

但这意味着Base需要了解每个Has接口,从而增加耦合。

让我们尝试一下访客模式:

struct BaseVisitor {
   void visitHasColor(HasColor& hasColor) = 0
};
struct Base {
   virtual void visit(BaseVisitor& visitor) = 0;
};

但是我们面临着同样的问题:每个Has类都需要添加到BaseVisitor中,从而导致更多的耦合和更多的重新编译(这是C ++,对Base的每次修改都意味着等待几分钟)。

如果我要支持插件,情况会变得更糟。插件无法修改BaseBaseVisitor,因此插件无法添加其他Has类。 (我意识到典型的C ++ RTTI在库之间可能无法很好地工作,但是我已经看到了一个自定义的RTTI系统,而Qt中的一个显然可以。)

我可以在保持代码可扩展性的同时避免使用RTTI / dynamic_cast吗?

This question似乎很相似。就我而言,我愿意采用其他方式来表示我的数据。

(在this one之类的C ++样式指南中,建议避免使用dynamic_cast。)

1 个答案:

答案 0 :(得分:3)

如果您收集了各种各样的东西,其中有些是HasColor,有些不是,那么您必须依靠dynamic_cast或类似的RTTI才能将对象用作{ {1}},否则您必须使用访问者模式来反转依赖关系,这会引入问题所在。

不需要使用HasColor或其他RTTI的解决方案是仅使用dynamic_cast个实例的集合。