views:

434

answers:

7

I'll phrase this in the form of an example to make it more clear.

Say I have a vector of animals and I want to go through the array and see if the elements are either dogs or cats?

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

}

I hope that's sort of clear. I know about typeid, but I don't really have any Dog object to compare it to and I would like to avoid creating a Dog object if I can.

Is there a way to do this? Thanks in advance.

+2  A: 

You can use dynamic_cast, as long as the vector contains Animal pointers.

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

but you should be aware that this is generally not the best practice. You should try to work with polymorphism, not against it. In other words, try to write a virtual Animal function that Dog and Cat override, and let the compiler automagically call the right one.

(Also, dynamic_cast is relatively slow, so too many of them will hinder performance; whereas a virtual function call is general just a single instruction.)

Jesse Beder
The one potential performance advantage of the dynamic cast is that it allows the "do whatever" to make non-virtual calls which can be inlined. It's not that the vcall is slow, itself, it's that it won't be inlined. But you'd have to measure to know which was faster in a given case.
Steve Jessop
+1  A: 

Are you sure you want to do that? What you're going to do is the exact contrary of polymorphism, and polymorphism is the best thing in object-oriented programming.

Loosely speaking: Don't do something if you animal is a Dog; let the Animal hierarchy know what to do when one of its objects is a Dog! :)

Federico Ramponi
A: 

You can use the typeid operator to do this, e.g.

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

This can't catch if it's a derived class of Dog, though. You can use a dynamic_cast for that. However, any use of typeid or dynamic_cast is often indicative of a design flaw. Usually, you don't need to know what your derived types are, and there's probably a better way that involves polymorphism. It's hard to give the right advice without a real example, though.

coppro
+6  A: 

As others has noted, you should neither use the typeid, nor the dynamic_cast operator to get the dynamic type of what your pointer points to. virtual functions were created to avoid this kind of nastiness.

Anyway here is what you do if you really want to do it (note that dereferencing an iterator will give you Animal* . So if you do **it you will get an 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 you can apply the typeid operator to types itself too, as shown above. You don't need to create an object for this. Also note the typeid way doesn't work if you pass it a pointer like typeid(*it) . Using it like that will give you just typeid(Animal*) which isn't useful.

Similar, dynamic_cast can be used:

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 that in both cases, your Animal type should be polymorph. That means it must have or inherited at least one virtual function.

Johannes Schaub - litb
Thanks a lot, very informative. I have some dynamic casts some other places in my program, but I just made it into a virtual function. It was in an area evaluating collision detection and I can't afford the performance hit. I'm still going to use typeid as in your example, because it's constant time
Chad
+1  A: 

If you really need your application-level to identify Dogs vs non-Dogs, you should avoid using RTTI (dynamic_cast and typeid), and make that knowledge explicit in your class hierarchy.

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

There are some minor performance benefits, but the primary benefit is exposing the necessary behavior in your class interface to maintenance programmers. RTTI (being as limited as it is) should not be required in order for a class hierarchy to function.

Now, along with what other people have said, it's likely that the isDog() function could be refactored into something that doesn't require knowledge of the entire hierarchy up-front (such as needsPoopCleanup()). Like everyone else said, you're losing the benefits of polymorphism if your application logic conditionally executes based on object type anyhow.

Tom
A base class shouldn't need to know its derived classes for the hierarchy to function. I totally agree that needsCleanup is better if that's possible, but isDog, isCat methods on Animal are just a home-brewed, inferior version of RTTI. If you can't do better you may as well use the real thing IMO.
Steve Jessop
It's only inferior in the sense that it is redundant with compiler-supported RTTI. If the distinction is required from a "business logic" level, it's still marginally more useful to provide a sort of "executable documentation" for maintenance programmers (rather than making them "figure it out").
Tom
A: 

using virtual functions:

As indicated by others responses, using virtual functions will often actually be sufficient enough, and is the "C++" way of thinking. Here is an example of using virtual functions:

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

If this will not be sufficient, then others have already posted answers which actually use conditional logic instead of polymerized logic.

Thr4wn
A: 

The accepted answer is correct, but you should know that there is another option as well that hasn't been mentioned. You could have a virtual function in the Animal class called "type()" which could return an int or a string (or any type that is comparable).

So for example:

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

This way you could just do:

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

The type function could return anything (an int unique to each derived type would work too, but strings illustrate it better).

Simply another option.

Evan Teran
Useful when your compiler can't do RTTI, or your system so constrained that you've switched it off in general and just want it for one small part of the hierarchy. If your hierarchy is small enough, you can use an int (bitset) allowing fast inheritance-checking with a mask and compare.
Steve Jessop