2011-01-10 17 views
8

En el siguiente código, mientras que la construcción de obj en case 1 tendríamos ninguna cómo construir derived clase demasiado pero es funciones miembro son inaccesibles para obj. Por lo tanto, mientras que el down- vaciado (es decir, en el caso 2), utilizando obj como fuente, de todos modos, ¿cómo se ha construido el derived en él. ¿Por qué requeriría que obj necesite ser polimórfico?C++ dynamic_cast - requisito polimórficos y downcasting

Si te confundí con mi descripción anterior, ¿Por qué en upcast obj no es necesario que sea polimórfico, pero mientras está abatido debe estar al usar dynamic_cast?

class base 
{ 
    public: 
    base() 
    { 
     cout<< " \n base constructor \n"; 
    } 
}; 

class derived:public base 
{ 
    public: 
    derived() 
    { 
     cout <<" \n derived constructor \n"; 
    } 
}; 

base *obj = dynamic_cast<base*> (new derived) ; // case 1: explicitly upcasting 
derived *OBJ = dynamic_cast<derived*> (obj) ; // case 2: error 

Gracias.

Respuesta

6

De 5.2.7/1 [expr.dynamic.cast]:

El resultado de la expresión dynamic_cast<T>(v) es el resultado de la conversión de la expresión v para escribir T.

[.. .]

Si T es "puntero a CV1 B" y V tiene el tipo "puntero a cv2 D" tal que B es una clase base de D, el resultado es un puntero a la sub-objeto B único del objeto D apuntado por v.

[...]

lo contrario, v será un puntero a o un lvalue de un tipo polimórfico.

La norma incluso proporciona el siguiente ejemplo que ilustra que el requisito de tipo polimórfico no representa derivada de la conversión de base:

struct B {}; 
struct D : B {}; 
void foo(D* dp) 
{ 
    B* bp = dynamic_cast<B*>(dp); // equivalent to B* bp = dp; 
} 
0

creo que de esta manera: cuando abatido, le está diciendo compilador que desea apuntar a un puntero de clase polimórfica. Pero cuando haces upcasting usando dynamic_cast, dice "ya me atrapaste cuando derivabas de mí, ¿por qué quieres volver a mí explícitamente usando dynamic_cast?" y por lo tanto el compilador da un error. Es decir, el compilador ve que en la clase Derivada, es una subparte del tipo Base y, por lo tanto, sabe que no es necesario que los programadores le pongan un puntero (y posiblemente creen estragos).

Espero que la explicación ayude.

+0

Nada mal si actualicé explícitamente, lo que claramente dice en los documentos estándar. – Mahesh

15

Para que dynamic_cast funcione, el objeto debe ser polimórfico. La razón para esto es que dynamic_cast necesita un lugar para almacenar la información de tipo que se utilizará para realizar el reparto, y lo hace almacenando la información junto con el vtable para la clase. Para que exista un vtable, debe hacer que al menos uno de sus métodos sea virtual.

La forma más sencilla de evitar esto es marcar el destructor de la clase base como virtual.

Upcasting (es decir, derivado a la base) no necesita un molde, ya que el compilador puede verificar que el molde funcionará en tiempo de compilación. Sin embargo, lo mismo no es cierto cuando downcasting.

+1

podrías abatir con 'static_cast' en este ejemplo. En el momento de la compilación, sabes que 'obj' contiene la clase correcta. El uso para dynamic_cast es cuando no se conoce en tiempo de compilación si el objeto es derivado de otro. Por ejemplo, cuando obtiene un puntero de una función y necesita comprobar si el objeto devuelto pertenece a una determinada clase. (Pero tienes razón, deben ser virtuales.) – RedX

0

dynamic_cast

  • Se utiliza para convertir un puntero de base en un puntero derivada. Si el puntero base no apunta a un objeto de el tipo del derivado, devuelve
  • Se utiliza para convertir una referencia base en una referencia derivada. Si la referencia no apunta a un objeto del derivado, arroja std :: bad_cast.
  • Se puede considerar el molde verificado equivalente a static_cast, ya que comprueba si el objeto apuntado a realmente es del tipo derivado.

Debe leer más sobre Dynamic_cast (con ejemplo) there.

0
B* b = new D(); 
D* d = dynamic_cast<D*>(b); 

En el ejemplo anterior mayoría de los compiladores sería implementar reparto dinámico mediante la comprobación de si el puntero vtable de los puntos B a la vtable de la clase o no D derivada. En caso afirmativo, simplemente devuelve la dirección de b como el valor de retorno, de lo contrario devuelve un nullptr. Esto es lo que posiblemente pasa detrás de las escenas cuando un dinámico reparto ejecuta: -

class car 
{ 
    public: 
    virtual void drive() 
    { 
     std::cout <<"car"<<std::endl; 
    } 
}; 
class toyota: public car 
{ 
    public: 
    virtual void drive() 
    { 
     std::cout <<"toyota"<<std::endl; 
    } 
}; 

class honda: public car 
{ 
    public: 
     virtual void drive() 
    { 
     std::cout <<"honda"<<std::endl; 
    } 
}; 

template <typename Tderived> 
Tderived* dynamicCast(void* pBase) 
{ 
    //compare the vptr of the class pointed by pBase with a temporary Tderived class. 
    //If vptr of pBase and vptr of Tderived() are pointing to the same vtable 
    //then it can be safely deduced that pBase is indeed pointing to an instance of Tderived 
    if (*(int**)pBase == *(int**)&Tderived()) 
    { 
     return (Tderived*)pBase; 
    } 
    else 
    { 
     return nullptr; 
    } 
} 


int main() 
{ 
    car* pCar; 
    honda hondaCar; 
    toyota toyotaCar; 

    pCar = &toyotaCar; 

    honda* pHonda = dynamicCast<honda>(pCar); 
    if (nullptr != pHonda) 
    { 
     pHonda->drive(); 
    } 
    else 
    { 
     toyota* pToyota = dynamicCast<toyota>(pCar); 
     if (nullptr != pToyota) 
     { 
      pToyota->drive(); 
     } 
    } 
} 

Ahora bien, si la clase no es polimórfico, no hay manera para que el compilador para encontrar si PCAR está apuntando a Honda o Toyota coche. Tenga en cuenta que esta es solo una de las formas de implementar dynamic_cast, ya que el estándar de C++ no habla nada acerca de los vtables.

Cuestiones relacionadas