2010-03-01 30 views
6

¿Cómo verifico en tiempo de ejecución si un objeto es del tipo ClassA o del tipo derivado ClassB? En un caso que tengo que manejar ambos casos por separadoBuscar el tipo derivado (C++)

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

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

    } 
} 

Tal vez podríamos decir que la clase derivada ClassB tiene algunas capacidades especiales. ¿Pero cómo lo hago sin cambiar la clase existente ClassA?

+2

La clase A no necesita saber nada sobre ClassB. –

+0

La comprobación de los tipos de clase, en general, es un indicador de un mal diseño. Ver el patrón de diseño * Visitante * para una alternativa. También estudie sobre polimorfismo. –

Respuesta

20

En general, es una muy mala idea activar ese tipo exacto. Al hacer esto, está estrechamente vinculado su método a las clases derivadas de ClassA. Deberías usar polimorfismo. Introduzca un método virtual en la clase A, anótelo en la clase B y simplemente llámelo en su método.

Incluso si me vi obligado a manejar la funcionalidad de la propia función externa por alguna razón, me gustaría hacer algo como:

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 { 

    } 
} 
+0

+1 - pero dice que no quiere/no puede cambiar ClassA, por lo que una función virtual está fuera de cuestión. –

+2

@Poita_: si no puede cambiar la clase base, tal vez debería haber ido con la composición en lugar de la herencia en primer lugar. –

+0

¿Qué pasa si él necesita polimorfismo de la clase base ...? –

4

¿Por qué no tener un método doSomething() en ClassB que maneja las capacidades adicionales de ClassB? Esta es la esencia del polimorfismo.

+2

No, el polimorfismo requiere que tenga el método 'doSomething' en' ClassA' (posiblemente como noop), anulándolo en 'ClassB'. El ejemplo incluso muestra por qué: no puede llamar a 'ClassB :: doSomething()' en un 'ClassA *'. – MSalters

2

slighlty diferente que lo que pidió

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

La sintaxis es la siguiente:

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

(Tenga en cuenta que esto sólo funciona dentro de las jerarquías de clases polimórficas Es decir, no tienen por qué ser las funciones virtuales. involucrado.)

Sin embargo, debe intentar evitar eso. ¿Qué es lo que necesita para eso no se puede resolver mediante la aplicación de funciones virtuales?

12

Utilice un dynamic_cast de la siguiente manera:

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

dynamic_cast<T *>(ptr) se return 0 en el caso ptr no es un puntero de tipo T, y devolverá un puntero de tipo T de lo contrario.

dynamic_cast generalmente se puede evitar y es un indicador de mal diseño/código. Si puede evitarlo, intente hacerlo, ya que requiere RTTI en su ejecutable final.

+4

Pero recuerde, ese argumento para dynamic_cast debe ser un puntero (o referencia) para una clase polimórfica (es decir, clase con al menos un método virtual). De lo contrario, dynamic_cast fallará. –

+2

Tendrás que probar el lanzamiento a 'DerivedClass' antes del lanzamiento a' BaseClass', ya que el molde 'BaseClass' tendrá éxito para cualquier tipo. –

+0

Buen punto :) He editado la respuesta. –

2

Otros han señalado que el cambio de tipos suele ser una mala idea, por lo que no lo haré. Si realmente tiene que hacerlo, puede utilizar el operador typeid para cambiar el tipo dinámico de un objeto:

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 es similar, pero las pruebas de la convertibilidad, no la igualdad:

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

Estos solo funcionan si ClassA es polimórfico (es decir, tiene al menos una función virtual).

Cuestiones relacionadas