2008-12-02 6 views
2

Je formulerai ceci sous la forme d'un exemple pour le rendre plus clair. Disons que j'ai un vecteur d'animaux et que je veux passer en revue le tableau et voir si les éléments sont des chiens ou des chats?Question d'identification de classe C++

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 un peu clair. Je sais à propos de typeid, mais je n'ai pas vraiment d'objet Dog à comparer et je voudrais éviter de créer un objet Dog si je le peux.

Est-il possible de le faire? Merci d'avance.

+0

Vous ne pouvez pas faire cela, il doit être un vecteur ou Animal *, pas Animal. –

Répondre

6

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

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

Remarque Vous pouvez également utiliser l'opérateur typeid pour le type lui-même, comme illustré 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 comme typeid(*it). L'utiliser comme ça vous donnera juste typeid(Animal*) ce qui n'est pas utile.

similaires, 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, le type d'animaux doit être polymorphes. Cela signifie qu'il doit avoir ou hérité d'au moins une fonction virtuelle.

+0

Merci beaucoup, très instructif. J'ai quelques distributions dynamiques d'autres endroits dans mon programme, mais je l'ai juste transformé en une fonction virtuelle. C'était dans une zone d'évaluation de détection de collision et je ne peux pas me permettre le coup de performance. Je vais toujours utiliser typeid comme dans votre exemple, car c'est un temps constant – Chad

2

Vous pouvez utiliser dynamic_cast, à condition que le vecteur contienne des pointeurs d'animaux. Mais vous devez savoir 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 virtuelle Animal que Dog et Cat outrepassent, et laissez le compilateur appeler automagiquement le droit.

(Aussi, dynamic_cast est relativement lent, trop donc d'entre eux nuire à la performance, alors qu'un appel de fonction virtuelle est juste une seule instruction générale.)

+0

Le seul avantage potentiel de la distribution dynamique est qu'elle permet au «faire n'importe quoi» de faire des appels non virtuels qui peuvent être en ligne. Ce n'est pas que le vcall soit lent, lui-même, c'est qu'il ne sera pas inline. Mais il faudrait mesurer pour savoir lequel était le plus rapide dans un cas donné. –

1

Etes-vous sûr de vouloir faire ça? Qu'est-ce que vous allez faire est le contraire exact du polymorphisme, et le polymorphisme est la meilleure chose dans la programmation orientée objet. Pour parler librement: Ne faites rien si votre animal est un chien; laisse la hiérarchie animale savoir quoi faire quand un de ses objets est un chien! :)

0

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

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

Cela ne peut pas attraper si elle est une classe dérivée de Dog, cependant. Vous pouvez utiliser un dynamic_cast pour cela. Cependant, toute utilisation de typeid ou dynamic_cast est souvent indicative d'un défaut de conception. Habituellement, vous n'avez pas besoin de savoir quels sont vos types dérivés, et il y a probablement un meilleur moyen qui implique le polymorphisme. Cependant, il est difficile de donner le bon conseil sans un véritable exemple.

1

Si vous avez vraiment besoin de votre niveau d'application pour identifier Dogs et non-Dogs, vous devez éviter d'utiliser RTTI (dynamic_cast et typeid) et rendre cette connaissance explicite dans votre hiérarchie de classe. Il existe quelques avantages de performance mineurs, mais le principal avantage est d'exposer le comportement nécessaire dans votre interface de classe aux programmeurs de maintenance. RTTI (étant aussi limité que cela) ne devrait pas être nécessaire pour qu'une hiérarchie de classe fonctionne. Maintenant, avec ce que d'autres personnes ont dit, il est probable que la fonction isDog() puisse être refactorisée en quelque chose qui ne nécessite pas la connaissance préalable de toute la hiérarchie (comme needsPoopCleanup()). Comme tout le monde l'a dit, vous perdez les avantages du polymorphisme si votre logique d'application s'exécute conditionnellement en fonction du type d'objet.

+0

Une classe de base ne devrait pas avoir besoin de connaître ses classes dérivées pour que la hiérarchie fonctionne. Je suis totalement d'accord que needsCleanup est meilleur si c'est possible, mais isDog, les méthodes isCat sur Animal sont juste une version inférée de RTTI.Si vous ne pouvez pas faire mieux, vous pouvez aussi bien utiliser la vraie chose IMO. –

+0

Il est seulement inférieur en ce sens qu'il est redondant avec le RTTI pris en charge par le compilateur. Si la distinction est requise à partir d'un niveau de «logique applicative», il est marginalement plus utile de fournir une sorte de «documentation exécutable» pour les programmeurs de maintenance (plutôt que de les faire «comprendre»). – Tom

0

en utilisant des fonctions virtuelles:

Comme indiqué par les réponses des autres, en utilisant des fonctions virtuelles seront souvent en fait être suffisant, et est la façon « C++ » de la pensée. Voici un exemple d'utilisation des 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 sera pas suffisant, alors que d'autres ont déjà posté des réponses qui utilisent effectivement la logique conditionnelle au lieu de la logique polymérisée.

0

La réponse acceptée est correcte, mais vous devez savoir qu'il existe une autre option qui n'a pas été mentionnée. Vous pourriez avoir une fonction virtuelle dans la classe Animal appelée "type()" qui pourrait renvoyer un int ou une chaîne (ou n'importe quel type qui est 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 pouvez simplement faire:

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

La fonction de type pourrait retourner quoi que ce soit (un unique, int à chaque type dérivé fonctionnerait aussi, mais les chaînes illustrent cest mieux).

Simplement une autre option.

+0

Utile lorsque votre compilateur ne peut pas faire de RTTI, ou que votre système est tellement contraint que vous l'avez désactivé en général et que vous ne le voulez que pour une petite partie de la hiérarchie. Si votre hiérarchie est suffisamment petite, vous pouvez utiliser un int (bitset) permettant une vérification rapide de l'héritage avec un masque et une comparaison. –