我应该改变我的设计以防止动态演员表吗?

Dou*_*Dan 8 c++ oop dynamic-cast

我已经阅读了几个关于C++动态转换的线程,所有人都声称它表明设计不好.在其他语言中,我在检查对象的类型时从未考虑过太多.我从不使用它作为多态性的替代,只有当强耦合看起来完全可以接受时.我经常遇到的这些情况之一:有一个列表(我在C++中使用std :: vector)的对象,都是从一个公共基类派生的.该列表由一个允许知道不同子类的对象管理(通常它是管理对象类中私有类的一个小层次).通过将它们保存在单个列表(数组,向量,...)中,我仍然可以从多态中受益,但是当一个操作意图作用于特定子类的对象时,我使用动态转换或类似的东西.

如果没有我缺少的动态强制转换或类型检查,是否存在针对此类问题的不同方法?我真的好奇那些不惜一切代价避免这些的程序员会如何处理它们.

如果我的描述过于抽象,我可以在C++中编写一个简单的例子(编辑:见下文).

class EntityContacts {
private:
  class EntityContact {
  private:
    virtual void someVirtualFunction() { };            // Only there to make dynamic_cast work
  public:
      b2Contact* m_contactData;
  };

  class InternalEntityContact : public EntityContact {
  public:
    InternalEntityContact(b2Fixture* fixture1, b2Fixture* fixture2){
        m_internalFixture1 = fixture1;
        m_internalFixture2 = fixture2;
    };

    b2Fixture* m_internalFixture1;
    b2Fixture* m_internalFixture2;
  };

  class ExternalEntityContact : public EntityContact {
  public:
    ExternalEntityContact(b2Fixture* internalFixture, b2Fixture* externalFixture){
        m_internalFixture = internalFixture;
        m_externalFixture = externalFixture;
    };

    b2Fixture* m_internalFixture;
    b2Fixture* m_externalFixture;
  };

  PhysicsEntity* m_entity;
  std::vector<EntityContact*> m_contacts;
public:
  EntityContacts(PhysicsEntity* entity)
  {
    m_entity = entity;
  }

  void addContact(b2Contact* contactData)
  {
    // Create object for internal or external contact
    EntityContact* newContact;
    if (m_entity->isExternalContact(contactData)) {
        b2Fixture* iFixture;
        b2Fixture* eFixture;
        m_entity->getContactInExFixtures(contactData, iFixture, eFixture);
        newContact = new ExternalEntityContact(iFixture, eFixture);
    }
    else
        newContact = new InternalEntityContact(contactData->GetFixtureA(), contactData->GetFixtureB());

    // Add object to vector
    m_contacts.push_back(newContact);
  };

  int getExternalEntityContactCount(PhysicsEntity* entity)
  {
    // Return number of external contacts with the entity
    int result = 0;
    for (int i = 0; i < m_contacts.size(); ++i) {
        ExternalEntityContact* externalContact = dynamic_cast<ExternalEntityContact*>(m_contacts[i]);
        if (externalContact != NULL && getFixtureEntity(externalContact->m_externalFixture) == entity)
            result++;
    }
    return result;
  }
};
Run Code Online (Sandbox Code Playgroud)

它是一个类的简化版本,用于在使用box2d物理的游戏中进行碰撞检测.我希望box2d的细节不会分散我想要展示的内容.我有一个非常相似的类'Event',它创建不同类型的事件处理程序,它们以相同的方式构造(使用基类EventHandler的子类而不是EntityContact).

Jer*_*fin 8

至少从我的角度来看,dynamic_cast存在是有原因的,有时候使用它是合理的.这可能是其中之一.

鉴于您描述的情况,一种可能的替代方法可能是在基类中定义您需要的更多操作,如果您为基类或其他不支持这些操作的类调用它们(可能是静默地),则将它们定义为失败操作.

真正的问题是以这种方式定义您的操作是否有意义.回到典型的基于动物的层次结构,如果你正在与鸟类合作,那么Bird类定义一个fly成员通常是明智的,对于少数无法飞行的鸟类来说,只是让它失败(理论上应该重命名)像这样的东西attempt_to_fly,但很少完成很多).

如果你看到很多这样的东西,它往往表明你的课程缺乏抽象 - 例如,你可能真的想要一个成员,而不是一个fly或者attempt_to_fly,travel这取决于个体动物来确定是否要做游泳,爬行,散步,飞行等


Bri*_*new 0

但是当一个操作要作用于特定子类的对象时,我使用动态强制转换或类似的东西

听起来对象建模不正确。您有一个包含子类实例的列表,但它们并不是真正的子类,因为您不能以相同的方式对它们进行操作(Liskov 等)。

一个可能的解决方案是扩展基类,以便拥有一组某些子类可以重写的无操作方法。但这听起来仍然不太正确。