Вопрос идентификации класса C ++ - PullRequest
2 голосов
/ 02 декабря 2008

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

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

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, если смогу.

Есть ли способ сделать это? Заранее спасибо.

Ответы [ 7 ]

6 голосов
/ 02 декабря 2008

Как уже отмечали другие, вы не должны использовать оператор typeid или dynamic_cast для получения динамического типа того, на что указывает ваш указатель. виртуальные функции были созданы, чтобы избежать этого вида злобности.

В любом случае, вот что вы делаете, если вы действительно хотите это сделать (обратите внимание, что разыменование итератора даст вам Animal*. Поэтому, если вы сделаете **it, вы получите 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. 
    }
}

Обратите внимание, что в обоих случаях ваш тип животных должен быть полиморфным. Это означает, что он должен иметь или унаследовать хотя бы одну виртуальную функцию.

2 голосов
/ 02 декабря 2008

Вы можете использовать dynamic_cast, если вектор содержит указатели животных.

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

1 голос
/ 02 декабря 2008

Если вам действительно нужен уровень приложения для идентификации собак против не-собак, вам следует избегать использования RTTI (dynamic_cast и typeid) и делать это явным в иерархии классов.

for (size_t i = 0; i != v.size(); ++i) {
    if (v[i]->isDog()) { v->cleanupPoop(); }
}

Есть некоторые незначительные преимущества в производительности, но основное преимущество заключается в предоставлении необходимого поведения в интерфейсе вашего класса программистам по обслуживанию. RTTI (будучи настолько ограниченным, насколько это возможно) не требуется для функционирования иерархии классов.

Теперь, наряду с тем, что говорили другие, вполне вероятно, что функция isDog() может быть преобразована в нечто, что не требует предварительного знания всей иерархии (например, needsPoopCleanup()). Как и все остальные, вы теряете преимущества полиморфизма, если логика вашего приложения все равно условно выполняется на основе типа объекта.

1 голос
/ 02 декабря 2008

Вы уверены, что хотите это сделать? То, что вы собираетесь сделать, является полной противоположностью полиморфизма, и полиморфизм - лучшая вещь в объектно-ориентированном программировании.

Грубо говоря: не делайте что-то , если ваше животное - Собака ; пусть иерархия Животных знает, что делать, когда один из ее объектов - Собака! :)

0 голосов
/ 02 декабря 2008

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

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

0 голосов
/ 02 декабря 2008

с использованием виртуальных функций:

Как указывалось в других ответах, использование виртуальных функций зачастую будет достаточно и является способом мышления "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;
}

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

0 голосов
/ 02 декабря 2008

Для этого можно использовать оператор typeid, например,

if (typeid(stuff[i].getClass())==typeid(Dog))

Это не может поймать, если это производный класс Dog, хотя. Вы можете использовать dynamic_cast для этого. Однако любое использование typeid или dynamic_cast часто свидетельствует о недостатке дизайна. Обычно вам не нужно знать, каковы ваши производные типы, и, вероятно, есть лучший способ, который включает полиморфизм Трудно дать правильный совет без реального примера.

...