当我不知道 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
}
}
}
我有一个存储“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
}
}
}