Domanda

Lo esprimerò sotto forma di un esempio per renderlo più chiaro.

Dire che ho un vettore di animali e voglio passare attraverso l'array e vedere se gli elementi sono cani o gatti?

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

}

Spero sia abbastanza chiaro. Conosco typeid, ma non ho davvero alcun oggetto Dog con cui confrontarlo e vorrei evitare di creare un oggetto Dog se posso.

C'è un modo per farlo? Grazie in anticipo.

È stato utile?

Soluzione

Come altri hanno notato, non dovresti usare né l'operatore typeid , né l'operatore dynamic_cast per ottenere il tipo dinamico di ciò che punta il tuo puntatore. sono state create funzioni virtuali per evitare questo tipo di cattiveria.

Comunque, ecco cosa fai se davvero vuoi farlo (nota che il dereferenziamento di un iteratore ti darà Animal * . Quindi se fai * * it otterrai 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
    }
}

Nota che puoi applicare l'operatore typeid anche ai tipi stessi, come mostrato sopra. Non è necessario creare un oggetto per questo. Nota anche che il modo typeid non funziona se gli passi un puntatore come typeid (* it) . Usarlo in questo modo ti darà solo typeid (Animal *) che non è utile.

Simile, dynamic_cast può essere usato:

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

Nota che in entrambi i casi, il tuo tipo di Animale dovrebbe essere polimorfico. Ciò significa che deve avere o ereditare almeno una funzione virtuale.

Altri suggerimenti

Puoi usare dynamic_cast , purché il vettore contenga puntatori di Animali.

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

ma dovresti essere consapevole che questa non è generalmente la migliore pratica. Dovresti provare a lavorare con il polimorfismo, non contro di esso. In altre parole, prova a scrivere una funzione virtuale Animal che Dog e Cat hanno la precedenza e lascia che il compilatore chiami automaticamente quella giusta.

(Inoltre, dynamic_cast è relativamente lento, quindi troppi di loro ostacoleranno le prestazioni; mentre una chiamata di funzione virtuale è in genere solo una singola istruzione.)

Sei sicuro di volerlo fare? Quello che farai è l'esatto contrario del polimorfismo e il polimorfismo è la cosa migliore nella programmazione orientata agli oggetti.

Parlando liberamente: non fare qualcosa se il tuo animale è un cane ; fai sapere alla gerarchia degli animali cosa fare quando uno dei suoi oggetti è un cane! :)

Se hai davvero bisogno del tuo livello di applicazione per identificare Dogs vs non-Dogs, dovresti evitare di usare RTTI ( dynamic_cast e typeid ) e rendere esplicita questa conoscenza in la tua gerarchia di classi.

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

Vi sono alcuni vantaggi in termini di prestazioni minori, ma il vantaggio principale è quello di esporre il comportamento necessario nell'interfaccia di classe ai programmatori di manutenzione. RTTI (essendo il più limitato possibile) non dovrebbe essere richiesto per far funzionare una gerarchia di classi.

Ora, insieme a ciò che hanno detto altre persone, è probabile che la funzione isDog () possa essere trasformata in qualcosa che non richiede la conoscenza dell'intera gerarchia in anticipo (come < code> needsPoopCleanup () ). Come tutti gli altri hanno detto, perdi i benefici del polimorfismo se la tua logica applicativa viene eseguita in modo condizionale in base al tipo di oggetto comunque.

Puoi usare l'operatore typeid per farlo, ad es.

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

Questo non può essere catturato se si tratta di una classe derivata di Dog . A tale scopo puoi utilizzare un dynamic_cast . Tuttavia, qualsiasi uso di typeid o dynamic_cast è spesso indicativo di un difetto di progettazione. Di solito, non è necessario sapere quali sono i tipi derivati ??e probabilmente esiste un modo migliore che comporta il polimorfismo. È difficile dare il consiglio giusto senza un vero esempio, tuttavia.

utilizzando le funzioni virtuali:

Come indicato da altre risposte, l'uso delle funzioni virtuali sarà spesso effettivamente sufficiente, ed è il "C ++" modo di pensare. Ecco un esempio dell'uso delle funzioni virtuali:

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

Se ciò non sarà sufficiente, altri hanno già pubblicato risposte che utilizzano effettivamente la logica condizionale invece della logica polimerizzata.

La risposta accettata è corretta, ma dovresti sapere che esiste anche un'altra opzione che non è stata menzionata. Potresti avere una funzione virtuale nella classe Animal chiamata " type () " che potrebbe restituire un int o una stringa (o qualsiasi tipo comparabile).

Quindi, per esempio:

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

In questo modo potresti semplicemente fare:

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

La funzione type potrebbe restituire qualsiasi cosa (funzionerebbe anche un int univoco per ogni tipo derivato, ma le stringhe lo illustrano meglio).

Semplicemente un'altra opzione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top