基础对象作为接受派生对象的函数的参数

时间:2019-03-21 22:34:44

标签: c++ function class inheritance arguments

我想编写一个函数,将“ Armor”类的对象作为参数,但是,当我调用该函数时,我改用基类“ Item”的对象。当然,我使用的这个对象即使仅被视为“物品”,也可能是“装甲”。仅当我确定它是“装甲”时,才想调用该函数。

就我而言,我将物品存储在矢量中(这些物品可以是装甲)。我想从向量中获取该物品并用它调用函数(装备该物品,我知道这是一件盔甲)。

class Item{
};

class Armor : public Item{
};

void equipArmor(Armor armor){ //Armor class argument
    //Equip the armor

}

int main(){
    vector<Item> items;
    Armor a;
    items.push_back(a);

    equipArmor(items[0]); //Call function with an "Item" as an argument (even though it is in fact also an "Armor")
}

2 个答案:

答案 0 :(得分:2)

问题

您的向量为Item。当您push_backArmor时,它将sliced变成Item。因此,向量中不再有Armor,而只有普通的Item

这就是为什么您的代码无法正常工作的原因。首先,您不能使用equipArmor()来调用Item函数,因为它期望Armor并且向下转换从不隐含。但是即使可以,您始终将传递Item值,而永远不会传递Armor值。

解决方案

要解决您的问题,您需要使用指针(更好的智能指针)或引用。

要能够使用polymorphic类型并进行运行时类型确定,首先需要在基类中至少具有一个虚函数:

class Item{
public: 
    virtual ~Item() {} 
};

现在让我们将向量设为shared pointers的向量。令人高兴的是,这些对象将确保在不再在任何共享指针中使用对象时将其销毁。因此,减少了内存管理的麻烦,而减少了rule of 3的麻烦:

vector<shared_ptr<Item>> items;

shared_ptr<Item> a = make_shared<Armor>();
items.push_back(a);
shared_ptr<Item> i = make_shared<Item>();
items.push_back(i);

equipArmor(items[0]); // lets just try the two possible cases
equipArmor(items[1]); 

最后,在您的函数中,您可以使用dynamic_pointer_cast安全的方式感知实类型并采取相应的行动:

void equipArmor(shared_ptr<Item> item){ //Armor class argument
    shared_ptr<Armor> a = dynamic_pointer_cast<Armor>(item);  
    if (a) {
        cout << "Armor"<<endl; 
    }
    else cout << "Item"<<endl;     
}

Online demo

备注

如果您的类型不是多态的,那么您将无法dynamic_pointer_cast。您仍然可以使用static_pointer_cast进行强制转换,但这是有风险的,因为它要求您确定转换的智能指针的类型正确。

如果您更喜欢原始指针,则可以应用相同的原理,但是请分别使用dynamic_caststatic_cast。但是,static_cast再次要求您绝对确定类型。如果您有一个充满随机项目的向量,怎么办?

答案 1 :(得分:1)

您想从基类(Item)转换为子类(Armor)。这是不可能的。

如果items是指向Item的指针的向量,则可以执行此操作。如果确定基础对象实际上是Item *,则可以将Armor *强制转换为Armour

int main(){
    std::vector<Item *> items;
    Armor a;
    items.push_back(&a);

    // This only works if items[0] points to an Armor object
    equipArmor(*static_cast<Armor *>(items[0]));
}