当我不知道 child 的类型时,C++ 从基础 class 调用 child class 函数

C++ Calling a child class function from a base class when I don't know the childs' type

我有一个存储“InventoryItem”的库存。

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

std::vector<InventoryItem> m_items;

我添加如下项目,m_inventory.addItem(bandage);

但是当我尝试调用从 Item base class 派生的 Bandages 的 use() 函数时,它调用了 Item class use() 函数。

已经在项目中声明了 class 像这样,

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

已经在Bandageclass中这样声明了,

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

在Bandage里面已经定义好了class是这样的,

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

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

它调用基础 class 'Item' use() 函数,而不是 'Bandage' 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 });
        }

    }
}

问题出在您的 addItem() 函数中,更准确地说是在此处:

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

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

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

很难看出哪里出了问题,但也许在循环中使用 auto 会把事情搞砸。作为快速测试,您可以尝试添加静态转换以验证 auto 没有搞乱虚拟函数的分辨率。

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

如果你真的想克隆你的对象而不是仅仅保留提供的指针(例如更好地使用auto_ptr),考虑添加一个clone() 函数到您的 Item 并导出 类:

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 });

对此有两种可能的解决方案:

首先,如果物品是无状态的,你可以这样实现,这样游戏中每种物品只有一个,并与所有玩家共享它的所有权(如果物品不是无状态的,那么使用物品的玩家会影响其他玩家的库存状态,或者在不同线程上使用时导致未定义的行为)。

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

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
        }
    }
}