Pregunta

Lo expresaré en forma de ejemplo para que quede más claro.

¿Digamos que tengo un vector de animales y quiero revisar el conjunto y ver si los elementos son perros o gatos?

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*/}

}

Espero que sea algo claro. Sé sobre typeid, pero realmente no tengo ningún objeto Dog para compararlo y me gustaría evitar crear un objeto Dog si puedo.

¿Hay alguna manera de hacer esto? Gracias de antemano.

¿Fue útil?

Solución

Como otros han señalado, no debe usar el typeid , ni el operador dynamic_cast para obtener el tipo dinámico de lo que apunta su puntero. Se crearon funciones virtuales para evitar este tipo de maldad.

De todos modos, esto es lo que debe hacer si realmente quiere hacerlo (tenga en cuenta que desreferenciar un iterador le dará Animal * . Entonces, si lo hace * * it obtendrá un Animal & amp; ):

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
    }
}

Tenga en cuenta que también puede aplicar el operador typeid a los tipos, como se muestra arriba. No necesita crear un objeto para esto. También tenga en cuenta que la forma typeid no funciona si le pasa un puntero como typeid (* it) . Usarlo así te dará solo typeid (Animal *) que no es útil.

Similar, dynamic_cast se puede usar:

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. 
    }
}

Tenga en cuenta que en ambos casos, su tipo de animal debe ser polimorfo. Eso significa que debe tener o heredar al menos una función virtual.

Otros consejos

Puede usar dynamic_cast , siempre que el vector contenga punteros de animales.

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
    }
}

pero debe tener en cuenta que, en general, esta no es la mejor práctica. Debes tratar de trabajar con el polimorfismo, no en contra de él. En otras palabras, intente escribir una función virtual Animal que Dog y Cat anulen, y deje que el compilador llame automáticamente a la correcta.

(Además, dynamic_cast es relativamente lento, por lo que muchos de ellos obstaculizarán el rendimiento; mientras que una llamada de función virtual es general, solo una sola instrucción).

¿Estás seguro de que quieres hacer eso? Lo que vas a hacer es exactamente lo contrario del polimorfismo, y el polimorfismo es lo mejor en la programación orientada a objetos.

Hablando libremente: No hagas algo si tu animal es un Perro ; ¡Deje que la jerarquía Animal sepa qué hacer cuando uno de sus objetos es un Perro! :)

Si realmente necesita su nivel de aplicación para identificar Perros vs no perros, debe evitar usar RTTI ( dynamic_cast y typeid ), y hacer explícito ese conocimiento en su jerarquía de clases.

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

Hay algunos beneficios menores de rendimiento, pero el beneficio principal es exponer el comportamiento necesario en su interfaz de clase a los programadores de mantenimiento. RTTI (por ser tan limitado como es) no debería ser necesario para que una jerarquía de clases funcione.

Ahora, junto con lo que otras personas han dicho, es probable que la función isDog () se pueda refactorizar en algo que no requiera conocimiento de toda la jerarquía por adelantado (como < code> needsPoopCleanup () ). Como todos los demás dijeron, está perdiendo los beneficios del polimorfismo si la lógica de su aplicación se ejecuta condicionalmente según el tipo de objeto de todos modos.

Puede usar el operador typeid para hacer esto, por ejemplo

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

Sin embargo, esto no puede detectarse si es una clase derivada de Dog . Puede usar un dynamic_cast para eso. Sin embargo, cualquier uso de typeid o dynamic_cast es a menudo indicativo de una falla de diseño. Por lo general, no necesita saber cuáles son sus tipos derivados, y probablemente haya una mejor manera de involucrar el polimorfismo. Sin embargo, es difícil dar el consejo correcto sin un ejemplo real.

utilizando funciones virtuales:

Como lo indican otras respuestas, el uso de funciones virtuales a menudo será suficiente, y es el "C ++" forma de pensar Aquí hay un ejemplo del uso de funciones virtuales:

#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;
}

Si esto no es suficiente, entonces otros ya han publicado respuestas que realmente usan lógica condicional en lugar de lógica polimerizada.

La respuesta aceptada es correcta, pero debe saber que también hay otra opción que no se ha mencionado. Podría tener una función virtual en la clase Animal llamada " type () " que podría devolver un int o una cadena (o cualquier tipo que sea comparable).

Entonces, por ejemplo:

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"; }
};

De esta manera, podrías hacer:

if(array[i]->type() == "dog") { }

La función type podría devolver cualquier cosa (un int exclusivo para cada tipo derivado también funcionaría, pero las cadenas lo ilustran mejor).

Simplemente otra opción.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top