Базовый объект в качестве аргумента функции, которая принимает производный объект - PullRequest
0 голосов
/ 22 марта 2019

Я хочу написать функцию, которая принимает в качестве аргумента объект из класса "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 ]

2 голосов
/ 22 марта 2019

проблема

У вас есть вектор Item. Когда вы push_back и Armor, он будет нарезан на Item. Таким образом, в векторе у вас больше нет Armor, а просто обычный Item.

Вот почему ваш код не будет работать. Во-первых, вы не можете вызывать вашу equipArmor() функцию с Item, так как она ожидает Armor, и снижение никогда не подразумевается. Но даже если бы вы могли, вы бы всегда передавали значение Item, а не значение Armor.

Решение

Чтобы решить вашу проблему, вам нужно работать с указателями (лучше умными указателями) или ссылками.

Первое, что вам понадобится для работы с полиморфными типами и выполнения определения типа во время выполнения, - это наличие хотя бы одной виртуальной функции в базовом классе:

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

Теперь давайте сделаем ваш вектор вектором из общих указателей . Приятно то, что они гарантируют, что объекты будут уничтожены, когда они больше не используются ни в каком общем указателе. Так меньше проблем с управлением памятью и меньше проблем с правилом 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;     
}

Демоверсия

Замечания

Если ваш тип не полиморфный, вы не можете dynamic_pointer_cast. Вы по-прежнему можете разыгрывать static_pointer_cast, но это рискованно, потому что это требует, чтобы вы точно знали, что приведенный умный указатель имеет правильный тип.

Если вы предпочитаете необработанные указатели, то примените те же принципы, но используйте dynamic_cast или static_cast соответственно. Но опять же, static_cast требует, чтобы вы были абсолютно уверены в типе. А как быть, если у вас есть вектор, полный случайных предметов?

1 голос
/ 22 марта 2019

Вы хотите привести из базового класса (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]));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...