C ++当我不知道孩子的时候,从基类调用子类函数'类型

时间:2015-04-05 17:07:33

标签: c++ polymorphism base-class

我有一个存储' InventoryItem的库存。

struct InventoryItem{
Item* item;
unsigned int quantity;};

std::vector<InventoryItem> m_items;

我添加以下内容,m_inventory.addItem(bandage);

但是当我尝试调用从Item基类派生的Bandages use()函数时,它会调用Item类use()函数。

它已在Item类中声明,如此,

   // ....
public:
   // ....
    virtual void use(Player&){}
   // ....

它已在Bandage类中声明,

   // ....
class Bandage : public Item{
public:
   // ....
    virtual void use(Player&);
   // ....

已在Bandage类中定义,如此,

void Bandage::use(Player& player)
{
    player.heal(35);
} 

当我尝试调用我的项目的use()函数时,例如,m_items.at(i).item->use(*m_player);

它调用基类&#39;项目&#39; use()功能,而不是&#39; Bandage&#39; use()功能。

编辑: 这是我的addItem函数,

void Inventory::addItem(const Item& item)
{
    if ((m_items.size() < m_capacity))
    {
        bool foundItem(false);
        for (auto invItem : m_items)
        {
            // if the item already exists, lets just increase the quantity
            if (invItem.item->getID() == item.getID())
            {
                foundItem = true;
                if (invItem.quantity < invItem.item->getInventoryQuantityLimit())
                {
                    ++invItem.quantity;
                }
            }
        }

        if (!foundItem){
            m_items.push_back(InventoryItem{ new Item(item), 1 });
        }

    }
}

4 个答案:

答案 0 :(得分:2)

问题出在你的addItem()函数中,更确切地说是在这里:

m_items.push_back(InventoryItem{ new Item(item), 1 });

Item*中的指针InventoryItem最终指向Item,而不指向Item派生的对象,即使item 派生对象。表达式new Item(item)不会创建派生对象,但会调用Item的复制构造函数(默认情况下,如果您还没有编写它),并将派生对象item作为参数。然后它在堆上创建Item对象,返回指向它的指针。

您最好使用创建所需项目的工厂方法,并摆脱有利于std::shared_ptr(或std::unique_ptr的原始指针,尽管可能在这里std::shared_ptr是更好)。

答案 1 :(得分:1)

如前所述,行new Item(item)创建了一个基础(Item),而不是派生对象。

如果你真的想克隆你的对象而不是仅保留提供的指针(例如,更好地使用auto_ptr),请考虑在{clone()函数中添加Item函数{1}}和派生类:

class Item {
    ...
    virtual Item* clone() { return new Item(*this);}
}

class Bandage : public Item {
    ...
    virtual Bandage* clone() { return new Bandage(*this); }
}

....
m_items.push_back(InventoryItem{ item.clone(), 1 });

答案 2 :(得分:0)

很难看出可能出现的问题,但是在你的循环中使用auto可能会搞砸了。作为快速测试,您可以尝试添加静态强制转换以验证自动不会破坏虚拟函数的分辨率。

答案 3 :(得分:0)

有两种可能的解决方案:

首先,如果项目是无状态的,您可以实现这一点,以便游戏中每种类型只有一个项目,并与所有玩家共享所有权(如果项目不是无状态,然后使用项目的玩家会影响其他玩家的库存状态,或者在不同的线程上使用时会导致未定义的行为。)

代码更改(对于无状态项目实施):

struct InventoryItem{
    std::shared_ptr<Item> item; // all inventory items using
                                // this (stateless) item, will share ownership
    unsigned int quantity;
};

void Inventory::addItem(const Item& item)
{
    if ((m_items.size() < m_capacity))
    {
        // everything here is the same as your code

        if (!foundItem){
            m_items.emplace_back( &item, 1 ); // share item
        }
    }
}

其次,如果项目不是无状态,则必须有一个虚拟克隆方法(允许每个专业化克隆它自己的专用类型。

实施变更:

class item {
    // rest of class here ...
public:
    virtual std::shared_ptr<item> clone() const = 0;
};

class bandage {
public:
    std::shared_ptr<item> clone() const override
    {
        return std::shared_ptr<item>{ new bandage{ *this } }; // use bandage copy constructor
                                     // and instantiate concrete item type
    }
};

void Inventory::addItem(const Item& item)
{
    if ((m_items.size() < m_capacity))
    {
        // everything here is the same as your code

        if (!foundItem){
            m_items.emplace_back( item.clone(), 1 ); // create specialized type
                                                  // through clone
        }
    }
}