Вопрос идентификации класса C ++
-
22-07-2019 - |
Вопрос
Я напишу это в форме примера, чтобы сделать его более понятным.
Скажем, у меня есть вектор животных, и я хочу просмотреть массив и посмотреть, являются ли элементы собаками или кошками?
class Dog: public Animal{/*...*/};
class Cat: public Animal{/*...*/};
int main()
{
vector<Animal*> stuff;
//cramming the dogs and cats in...
for(/*all elements in stuff*/)
//Something to the effect of: if(stuff[i].getClass()==Dog) {/*do something*/}
}
Надеюсь, это понятно. Я знаю о typeid, но у меня нет ни одного объекта Dog для сравнения, и я бы хотел избежать создания объекта Dog, если смогу.
Есть ли способ сделать это? Заранее спасибо.
Решение
Как уже отмечали другие, вы не должны ни использовать typeid
, ни оператор dynamic_cast
, чтобы получить динамический тип того, на что указывает ваш указатель. виртуальные функции были созданы, чтобы избежать этого вида злобности. Р>
В любом случае, вот что вы делаете, если вы действительно хотите это сделать (обратите внимание, что разыменование итератора даст вам Animal *
. Поэтому, если вы сделаете * *
вы получите Animal &
):
for(std::vector<Animal*>::iterator it = v.begin(); it != v.end(); ++it) {
if(typeid(**it) == typeid(Dog)) {
// it's a dog
} else if(typeid(**it) == typeid(Cat)) {
// it's a cat
}
}
Обратите внимание, что вы можете применять оператор typeid
и к самим типам, как показано выше. Вам не нужно создавать объект для этого. Также обратите внимание, что метод typeid не работает, если вы передаете ему указатель типа typeid (* it)
. Такое использование даст вам только typeid (Animal *)
, что бесполезно.
Аналогично, dynamic_cast
можно использовать:
for(std::vector<Animal*>::iterator it = v.begin(); it != v.end(); ++it) {
if(Dog * dog = dynamic_cast<Dog*>(*it)) {
// it's a dog (or inherited from it). use the pointer
} else if(Cat * cat = dynamic_cast<Cat*>(*it)) {
// it's a cat (or inherited from it). use the pointer.
}
}
Обратите внимание, что в обоих случаях ваш тип Animal должен быть полиморфным. Это означает, что он должен иметь или унаследовать хотя бы одну виртуальную функцию.
Другие советы
Вы можете использовать dynamic_cast
, если вектор содержит указатели Animal.
vector <Animal *> stuff;
for(int i=0;i<stuff.size();i++) {
Dog *pDog = dynamic_cast <Dog *> (stuff[i]);
if(pDog) {
// do whatever with the dog
}
Cat *pCat = dynamic_cast <Cat *> (stuff[i]);
if(pCat) {
// and so on
}
}
но вы должны знать, что это, как правило, не лучшая практика. Вы должны попытаться работать с полиморфизмом, а не против него. Другими словами, попробуйте написать виртуальную функцию Animal
, которую переопределяют Dog
и Cat
, и дайте компилятору автоматически вызвать нужную функцию. р>
(Кроме того, dynamic_cast
является относительно медленным, поэтому слишком многие из них будут снижать производительность; тогда как вызов виртуальной функции, как правило, представляет собой всего лишь одну инструкцию.)
Вы уверены, что хотите это сделать? То, что вы собираетесь сделать, является полной противоположностью полиморфизма, и полиморфизм - лучшая вещь в объектно-ориентированном программировании.
Грубо говоря: не делайте что-нибудь , если ваше животное - Собака ; пусть иерархия Животных знает, что делать, когда один из ее объектов - Собака! :) Р>
Если вам действительно нужен уровень приложения для идентификации собак против не-собак, вам следует избегать использования RTTI ( dynamic_cast
и typeid
) и сделать это знание явным в ваша иерархия классов.
for (size_t i = 0; i != v.size(); ++i) {
if (v[i]->isDog()) { v->cleanupPoop(); }
}
Есть некоторые незначительные преимущества в производительности, но основное преимущество заключается в предоставлении необходимого поведения в интерфейсе вашего класса программистам по обслуживанию. RTTI (будучи настолько ограниченным, насколько это возможно) не требуется для функционирования иерархии классов.
Теперь, наряду с тем, что говорили другие, вполне вероятно, что функция isDog ()
может быть реорганизована во что-то, что не требует предварительного знания всей иерархии (например, < код> needsPoopCleanup () код>). Как и все остальные, вы теряете преимущества полиморфизма, если логика вашего приложения все равно условно выполняется на основе типа объекта.
Для этого вы можете использовать оператор typeid
, например
if (typeid(stuff[i].getClass())==typeid(Dog))
Это не может поймать, если это производный класс Dog
. Для этого вы можете использовать dynamic_cast
. Однако любое использование typeid
или dynamic_cast
часто свидетельствует о недостатке дизайна. Обычно вам не нужно знать, каковы ваши производные типы, и, вероятно, есть лучший способ, который включает полиморфизм. Трудно дать правильный совет без реального примера.
с использованием виртуальных функций:
Как указывалось в других ответах, использование виртуальных функций зачастую будет достаточным, и это "C ++" способ мышления. Вот пример использования виртуальных функций:
#include<iostream>
#include<vector>
using namespace std;
/////////////
class Animal {
public:
virtual void move() { cout << "animal just moved" << endl; }
};
class Dog : public Animal {
public:
void move() { cout << "dog just moved" << endl; }
};
class Cat : public Animal {
public:
void move() { cout << "cat just moved" << endl; }
};
void doSomethingWithAnimal(Animal *a) {
a->move();
}
/////////////
int main() {
vector<Animal*> vec;
vector<Animal*>::iterator it;
Animal *a = new Animal;
Dog *d = new Dog;
Cat *c = new Cat;
vec.push_back(a);
vec.push_back(d);
vec.push_back(c);
it = vec.begin();
while( it != vec.end() ) {
doSomethingWithAnimal(*it);
it++;
}
return 0;
}
Если этого будет недостаточно, то другие уже опубликовали ответы, которые фактически используют условную логику вместо полимеризованной логики.
Принятый ответ правильный, но вы должны знать, что есть и другой вариант, который не был упомянут. В классе Animal может быть виртуальная функция с именем " type () " который мог бы вернуть int или строку (или любой тип, который сопоставим).
Так, например:
class Animal {
/*...*/
public:
virtual std::string type() const { return "animal"; }
};
class Dog: public Animal{
/*...*/
public:
virtual std::string type() const { return "dog"; }
};
class Cat: public Animal{
/*...*/
public:
virtual std::string type() const { return "cat"; }
};
Таким образом, вы могли бы просто сделать:
if(array[i]->type() == "dog") { }
Функция type может возвращать что угодно (int, уникальный для каждого производного типа, тоже будет работать, но строки иллюстрируют это лучше).
Просто еще один вариант.