所有派生类的通用接口

时间:2014-06-08 09:05:05

标签: c++

我有基类Item,它存储一些数据并通过访问器授予对它的访问权限,如:

class Item{
  (...)
public:
  int get_value();
  double get_weight();
  ItemMaterial get_material();
  (...)
}

然后我有了像Weapon,Armor这样的派生类,它们增加了一些额外的数据:

class Weapon : public Item {
(...)
public:
  int get_dmg();
  (...)
}

我将这些物品存放在某个容器中:

std::vector<Item*> inventory;

接下来是接口的问题 - 如何访问派生类数据?我在想,并有3个想法:

1。单独的界面

每个派生类都会添加其数据,如上所示,然后使用dynamic_cast:

Item *item = new Weapon;
int dmg = dynamic_cast<Weapon*>(item)->get_dmg();

2。通用接口类

创建一个包含所有访问者的接口类:

ItemInterface{
public:
  virtual int get_value() = 0;     //Item interface
  virtual double get_weight() = 0;
  (..)
  virtual int get_dmg() = 0;       //Weapon interface
  (...)
}

然后是这样的事情:

Item : public ItemInterface{ (...) }

Weapon : public Item { (...) }

最后我们可以访问数据:

Item *item = new Weapon;
int dmg = item->get_dmg();

第3。与模板和枚举相结合

这个想法可能有点奇怪:-)但是:

使用所有项目数据实现枚举:

enum class ItemData{
  Value,
  Weight,
  Material,  //Item data
  (...)
  Damage,    //Weapon data
  (...)
  Defense,   //armor data etc.
  (...)
  Null
}

在基类中有一些模板函数如下:

template<typename T>
T get_data(ItemData data){
  switch(data){
    case ItemData::Value: return _value; break;
    case ItemData::Damage: return _dmg; break;
    (...)
  }
}

并访问以下数据:

Item *item = new Weapon;
ind dmg = item->get_data<int>(ItemData::Damage);

===

您认为应如何做?我会很感激任何建议!

的问候。

2 个答案:

答案 0 :(得分:2)

你的第二个和第三个选项显然是的方式 - 每当你添加一个新类型的项目时,你还必须更改基类或枚举 - 这绝对不是什么如果您的代码中需要任何基本形式的可维护性,那么您希望这样做。

  

接下来是接口问题 - 如何访问派生类数据

首先,您必须考虑“您的代码将在何处执行此操作”?处理整个inventory的大多数代码只应将内容用作Item*,仅使用Item类中的函数。

如果你有专门处理Weapon个对象的代码,那么创建Weapon个对象(并插入inventory)的地方也可以将它们添加到另一个变量中,也许

形式的武器清单
std::vector<Weapon*> weapons;

或类Weapon*的成员变量Warrior或类似的东西(但要注意,您现在将有两个指向相同对象的指针,因此您必须考虑所有权)。因此,仅处理武器的代码(例如,Warrior的成员函数)不会访问inventory来获取Weapon对象,它将始终使用Weapon*直。

如果由于某些原因,您必须编写一些代码来处理库存中所有武器的某些内容,那么请编写一个函数,使用Weapon提取所有dynamic_cast个对象(甚至更好) :使它成为迭代器函数),并在需要访问所有武器时重用此函数。因此,您不会使用动态强制转换混乱您的代码,但请将其保留在一个位置。

编辑:另一种选择(避免动态演员)使用访客模式,请参阅this post。但我真的不喜欢那篇文章的答案,在提交的表格中它将暗示循环依赖“Base - &gt; Visitor - &gt; Derived - &gt; Base”,这是恕我直言,这是一个糟糕的设计。

答案 1 :(得分:1)

ValueType Weapon::getProprtyValue( PropertyType id ) {
   switch( id ) {
      case kWeaponProperty01: return m_weaponProperty01;
      ...
      default: return Item::getPropertyValue( id );
   }
}

您可以制作某种通用访问器方法,虽然它有一些限制,但它可以非常方便,特别是在内容编辑器,序列化等情况下。