views:

204

answers:

6

How do I check at runtime if an object if of type ClassA or of derived type ClassB? In one case I have to handle both instances separately

ClassA* SomeClass::doSomething ( ClassA* )
{
    if( /* parameter is of type base class */) {

    } else if { /* derived class */ ) {

    }
}

Maybe I could say that the derived class ClassB has some special capabilities. But how do I do that without changing the existing class ClassA ?

+5  A: 

Why not have a doSomething() method on ClassB that handles ClassB's extra capabilities? This is the essence of polymorphism.

Skilldrick
No, polymorphism requires you to have the `doSomething` method on `ClassA` (possibly as a noop), overriding it in `ClassB`. The example even shows why: you can't call `ClassB::doSomething()` on a `ClassA*`.
MSalters
+2  A: 

Slighlty different that what you asked for

ClassB* b;
if ((b = dynamic_cast<ClassB*>(ptr)) == 0) {
    // not a classB*
} else {
    // a classB* in b
}
AProgrammer
+3  A: 

The syntax is this:

ClassA* SomeClass::doSomething ( ClassA* pa )
{
    ClassB* pb = dynamic_cast<ClassB*>(pa);
    if( pb ) ...

(Note that this only works within polymorphic class hierarchies. That is, there have to be virtual functions involved.)

However, you should try to avoid that. What do you need it for that cannot be solved by applying virtual functions?

sbi
+12  A: 

It's generally a very bad idea to switch on the exact type like that. By doing this, you are tightly coupling your method to derived classes of ClassA. You should use polymorphism. Introduce a virtual method in class A, override it in class B and simply call it in your method.

Even if I was forced to handle the functionality in the external function itself for some reason, I would do something like:

class ClassA { 
  public: virtual bool hasSpecificFunctionality() { return false; }
};

class ClassB : public ClassA {
  public: virtual bool hasSpecificFunctionality() { return true; }
};

ClassA* SomeClass::doSomething ( ClassA* arg )
{
    if (arg->hasSpecificFunctionality()) {

    } else {

    }
}
Mehrdad Afshari
+1 - but he does say that he doesn't want to/can't change ClassA, so a virtual function is out of the question.
Peter Alexander
@Poita_: If you can't change the base class, maybe you should have gone with composition instead of inheritance in the first place.
Mehrdad Afshari
What if he needs polymorphism from the base class...?
Peter Alexander
@Poita_: It's hard to answer this question without looking at the special case. Most of the time, I'd split up functionality in two classes. One of them will be derived from `ClassA` to override the method in it and compose one from the other. Basically, I'll use composition to provide the extended functionality and inheritance to provide the customized behavior.
Mehrdad Afshari
+6  A: 

Use a dynamic_cast as follows:

ClassA* SomeClass::doSomething(ClassA *a)
{
    if (dynamic_cast<DerivedClass *>(a)) {
        ....
    } else if (dynamic_cast<BaseClass *>(a)) {
        ....
    }
 }

dynamic_cast<T *>(ptr) will return 0 in case ptr is not a pointer of type T, and will return a pointer of type T otherwise.

dynamic_cast can usually be avoided and is an indicator of bad design / code. If you can avoid it, try to do so, as it requires RTTI in your final executable.

Ton van den Heuvel
But remember, that argument to dynamic_cast must be a pointer (or reference) to a polymorphic class (i.e. class with at least one virtual method). Otherwise dynamic_cast will fail.
Tadeusz Kopec
You'll need to try the cast to `DerivedClass` before the cast to `BaseClass`, as the `BaseClass` cast will succeed for either type.
Mike Seymour
Good point :) I've edited the answer.
Ton van den Heuvel
+1  A: 

Others have pointed out that switching on types is usually a bad idea, so I won't. If you really have to do it, you can use the typeid operator to switch on the dynamic type of an object:

ClassA* SomeClass::doSomething ( ClassA* a )
{
    if (typeid(*a) == typeid(ClassA)) {
        /* parameter is of type base class */
    } else if (typeid(*a) == typeid(ClassB)) {
        /* a specific derived class */ 
    } else {
        /* some other derived class */
    }
}

dynamic_cast is similar, but tests for convertibility, not equality:

ClassA* SomeClass::doSomething ( ClassA* a )
{
    if (ClassB *b = dynamic_cast<classB*>(a)) {
        /* parameter is, or is derived from, ClassB */
    } else {
        /* parameter is, or is derived from, ClassA but not ClassB */ 
    }
}

These only work if ClassA is polymorphic (that is, it has at least one virtual function).

Mike Seymour