Question

Je vais formuler ceci sous la forme d'un exemple pour le rendre plus clair.

Disons que j'ai un vecteur d'animaux et que je veux parcourir le tableau et voir si les éléments sont des chiens ou des chats?

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

}

J'espère que c'est clair. Je connais bien le typeid, mais je n'ai pas vraiment d'objet Dog à comparer et j'aimerais éviter de créer un objet Dog si je le peux.

Y at-il un moyen de faire cela? Merci d'avance.

Était-ce utile?

La solution

Comme d'autres l'ont noté, vous ne devez ni utiliser typeid , ni l'opérateur dynamic_cast pour obtenir le type dynamique de votre pointeur. des fonctions virtuelles ont été créées pour éviter ce genre de méchanceté.

Quoi qu'il en soit, voici ce que vous faites si vous réellement voulez le faire (notez que le déréférencement d'un itérateur vous donnera Animal * . Donc, si vous faites * * cela vous obtiendrez un 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
    }
}

Notez que vous pouvez également appliquer l'opérateur typeid aux types lui-même, comme indiqué ci-dessus. Vous n'avez pas besoin de créer un objet pour cela. Notez également que la méthode typeid ne fonctionne pas si vous lui passez un pointeur tel que typeid (* it) . En l'utilisant comme cela, vous obtiendrez simplement typeid (Animal *) , ce qui n'est pas utile.

Similaire, dynamic_cast peut être utilisé:

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

Notez que dans les deux cas, votre type d’Animal doit être polymorphe. Cela signifie qu'il doit avoir ou hérité d'au moins une fonction virtuelle.

Autres conseils

Vous pouvez utiliser dynamic_cast , tant que le vecteur contient des pointeurs d'animaux.

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

mais sachez que ce n'est généralement pas la meilleure pratique. Vous devriez essayer de travailler avec le polymorphisme, pas contre. En d’autres termes, essayez d’écrire une fonction Animal virtuelle que Dog et Cat écrasent, et laissez le compilateur appeler automatiquement le bon.

(En outre, dynamic_cast est relativement lent, un trop grand nombre d'entre eux entravera les performances; alors qu'un appel de fonction virtuelle est en général une simple instruction.)

Êtes-vous sûr de vouloir faire cela? Ce que vous allez faire est tout le contraire du polymorphisme, et le polymorphisme est la meilleure chose en programmation orientée objet.

En gros, ne faites pas quelque chose si votre animal est un chien ; Faites savoir à la hiérarchie animale ce qu'il faut faire lorsqu'un de ses objets est un chien! :)

Si vous avez vraiment besoin que votre niveau d'application identifie les chiens par rapport aux non-chiens, évitez d'utiliser RTTI ( dynamic_cast et typeid ), et explicitez ces connaissances dans votre hiérarchie de classe.

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

Il existe quelques avantages mineurs en termes de performances, mais le principal avantage est d'exposer le comportement nécessaire de votre interface de classe aux programmeurs de maintenance. RTTI (étant aussi limité qu'il soit) ne devrait pas être nécessaire pour qu'une hiérarchie de classes fonctionne.

Maintenant, avec ce que d'autres personnes ont dit, il est probable que la fonction isDog () pourrait être reformulée en quelque chose qui n'exige pas la connaissance de toute la hiérarchie à l'avance (telle que < code> needsPoopCleanup () ). Comme tout le monde le dit, vous perdez les avantages du polymorphisme si la logique de votre application s'exécute de manière conditionnelle en fonction du type d'objet.

Vous pouvez utiliser l'opérateur typeid pour ce faire, par exemple

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

Cela ne peut pas être attrapé s'il s'agit d'une classe dérivée de Dog , cependant. Vous pouvez utiliser un dynamic_cast pour cela. Cependant, toute utilisation de typeid ou de dynamic_cast est souvent révélatrice d'un défaut de conception. Généralement, vous n'avez pas besoin de connaître vos types dérivés, et il existe probablement une meilleure façon de faire appel au polymorphisme. Il est difficile de donner les bons conseils sans un exemple concret.

à l'aide de fonctions virtuelles:

Comme indiqué par d’autres réponses, l’utilisation de fonctions virtuelles suffira souvent, c’est le "C ++". façon de penser. Voici un exemple d'utilisation de fonctions virtuelles:

#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 cela ne suffit pas, d’autres ont déjà posté des réponses utilisant la logique conditionnelle au lieu de la logique polymérisée.

La réponse acceptée est correcte, mais vous devez savoir qu’une autre option n’a pas non plus été mentionnée. Vous pouvez avoir une fonction virtuelle dans la classe Animal appelée "type ()". qui pourrait renvoyer un int ou une chaîne (ou tout type comparable).

Ainsi, par exemple:

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 cette façon, vous pourriez simplement faire:

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

La fonction type peut renvoyer n'importe quoi (un int unique à chaque type dérivé fonctionnerait aussi, mais les chaînes l'illustrent mieux).

Simplement une autre option.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top