Вопрос

Я напишу это в форме примера, чтобы сделать его более понятным.

Скажем, у меня есть вектор животных, и я хочу просмотреть массив и посмотреть, являются ли элементы собаками или кошками?

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, уникальный для каждого производного типа, тоже будет работать, но строки иллюстрируют это лучше).

Просто еще один вариант.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top