Pergunta

Vou frase esta na forma de um exemplo para torná-lo mais claro.

dizer que tenho um vetor de animais e eu quero ir através da matriz e ver se os elementos são ou cães ou 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 é uma espécie de claro. Eu sei sobre typeid, mas eu realmente não tenho qualquer objeto Dog para compará-lo com e eu gostaria de evitar a criação de um objeto Dog se eu puder.

Existe uma maneira de fazer isso? Agradecemos antecipadamente.

Foi útil?

Solução

Como outros observou, você deve não usar o typeid, nem o operador dynamic_cast para obter o tipo dinâmico do que seus ponteiro aponta. funções virtuais foram criadas para evitar este tipo de maldade.

De qualquer forma aqui está o que você faz se você realmente quero fazer isso (note que dereferencing um iterador lhe dará Animal* Então, se você faz **it você receberá um 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
    }
}

Note que você pode aplicar o operador typeid para si tipos também, como mostrado acima. Você não precisa criar um objeto para isso. Além disso, observe a maneira typeid não funciona se você passar um ponteiro como typeid(*it). Usando-o como que lhe dará apenas typeid(Animal*) que não é útil.

Semelhante, dynamic_cast pode ser usado:

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

Note que em ambos os casos, o seu tipo de animal deve ser polimorfo. Isso significa que ele deve ter ou herdado pelo menos uma função virtual.

Outras dicas

Você pode usar dynamic_cast, enquanto o vector contém ponteiros animais.

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

mas você deve estar ciente de que isso geralmente não é a melhor prática. Você deve tentar trabalhar com o polimorfismo, não contra ela. Em outras palavras, tentar escrever uma função Animal virtual que Dog e Cat override, e deixar o compilador automagicamente chamar o caminho certo.

(Além disso, dynamic_cast é relativamente lento, então muitos deles irá prejudicar o desempenho;. Considerando uma chamada de função virtual é em geral apenas uma única instrução)

Você tem certeza que quer fazer isso? O que você vai fazer é exatamente o contrário do polimorfismo e polimorfismo é a melhor coisa na programação orientada a objeto.

Genericamente falando: não fizer algo se você animal é um cão ; deixe a hierarquia animal sabe o que fazer quando um de seus objetos é um cão! :)

Se você realmente precisa de seu em nível de aplicativo para identificar Cães vs não-Cães, você deve evitar usar RTTI (dynamic_cast e typeid), e fazer com que o conhecimento explícito em sua hierarquia de classe.

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

Existem alguns benefícios de desempenho menor, mas o principal benefício está expondo o comportamento necessário em sua interface de classe para programadores de manutenção. RTTI (sendo tão limitado como é) não deve ser necessário para que uma hierarquia de classes para a função.

Agora, junto com o que outras pessoas disseram, é provável que a função isDog() poderia ser reformulado em algo que não requer conhecimento de toda a hierarquia up-front (como needsPoopCleanup()). Como qualquer outro, você está perdendo os benefícios do polimorfismo se a lógica do aplicativo executa condicionalmente com base no tipo de objeto de qualquer forma.

Você pode usar o operador typeid de fazer isso, por exemplo.

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

Isto não pode pegar se é uma classe derivada de Dog, no entanto. Você pode usar um dynamic_cast para isso. No entanto, qualquer uso de typeid ou dynamic_cast é muitas vezes indicativo de uma falha de projeto. Normalmente, você não precisa saber o que seus tipos derivados são, e provavelmente há uma maneira melhor que envolve polimorfismo. É difícil dar o conselho certo sem um exemplo real, no entanto.

usando funções virtuais:

Como indicado por outras respostas, usando funções virtuais, muitas vezes, na verdade, ser suficiente suficiente, e é o "C ++" maneira de pensar. Aqui está um exemplo do uso de funções virtuais:

#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 isto não será suficiente, em seguida, outros já respostas que realmente usam lógica condicional em vez de lógica polimerizado postou.

A resposta aceita é correto, mas você deve saber que não há outra opção bem que não tenha sido mencionado. Você poderia ter uma função virtual na classe animal chamado "type ()", que poderia retornar um int ou uma string (ou qualquer tipo que é comparável).

Assim, por exemplo:

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

Desta forma, você poderia simplesmente fazer:

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

A função tipo poderia retornar nada (um int único para cada tipo derivado iria trabalhar muito, mas cordas ilustrá-la melhor).

Simplesmente outra opção.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top