2010-09-19 14 views

Respuesta

80

Para comprender el sistema de conversión, debe sumergirse en el modelo de objetos.

La representación clásica de un sencillo modelo de jerarquía de contención es: que si B deriva de A entonces el objeto B, de hecho, contiene un subobjeto A junto con sus propios atributos.

Con este modelo, downcasting es una manipulación de puntero simple, por un desplazamiento conocido en tiempo de compilación que depende del diseño de memoria de B.

Esto es lo que static_cast hacer: un molde estático es apodado estática porque el cálculo de lo que es necesario para el reparto se realiza en tiempo de compilación, ya sea un apuntador aritmética o conversiones (*).

Sin embargo, cuando entra en juego la herencia virtual las cosas tienden a ser un poco más difíciles. El problema principal es que con la herencia virtual todas las subclases comparten una misma instancia del subobjeto. Para hacer eso, B tendrá un puntero a A, en lugar de A, y el objeto de clase base A se creará una instancia fuera de B.

Por lo tanto, es imposible en el tiempo de compilación deducir la aritmética del puntero necesaria: depende del tipo de tiempo de ejecución del objeto.

Siempre que haya una dependencia de tipo de tiempo de ejecución, necesita RTTI (información de tipo de tiempo de ejecución), y hacer uso de RTTI para conversiones es el trabajo de dynamic_cast.

En resumen:

  • tiempo de compilación abatido: static_cast
  • tiempo de ejecución abatido: dynamic_cast

los otros dos son también en tiempo de compilación arroja, pero son tan específicas que es fácil recordar para qué son ... y huelen mal, así que mejor no usarlos de todos modos.

(*) Según lo observado por @curiousguy en los comentarios, esto solo vale para downcasting. Un static_cast permite el upcasting independientemente de la herencia virtual o simple, aunque el lanzamiento también es innecesario.

+4

¡Buena respuesta que me hizo entender cómo funciona la herencia virtual!+1 – undu

+0

Me gusta su respuesta, pero aparentemente el OP estaba preguntando acerca de un error para DOWNCASTING en lugar de upcasting. – h9uest

+0

@ h9uest: Gracias por señalar el error, cambié "up-casting" a "downcasting" y ahora todo está bien. –

11

Por lo que yo sé, necesita usar dynamic_cast porque la herencia es virtual y está realizando una transmisión.

6

No puede usar static_cast en esta situación porque el compilador no conoce el desplazamiento de B relativo a A en tiempo de compilación. El desplazamiento debe calcularse en tiempo de ejecución en función del tipo exacto del objeto más derivado. Por lo tanto, debe usar dynamic_cast.

+0

Al convertir una base derivada en virtual, puede usar 'static_cast'. – curiousguy

+0

@curiousguy: sí, pero la pregunta es sobre convertir bases en derivadas. – ybungalobill

+0

No está claro por qué su argumento no se aplica a derivada a base también. – curiousguy

4

Sí, tiene que usar un dynamic_cast, pero tendrá que hacer que la clase base sea polimórfica, p. Ej. agregando un dtor virtual.

+1

o agregando al menos un método virtual. – Liton

4

Según documentos estándar,

Sección 5.2.9 - 9, por vaciado estático,

An rvalue of type “pointer to cv1 B,” where B is a class type, can be converted to an rvalue of type “pointer to cv2 D,” where D is a class derived (clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class of D nor a base class of a virtual base class of D.

Por lo tanto, no es posible y se debe utilizar dynamic_cast ...

1

$5.2.9/2- "An expression e can be explicitly converted to a type T using a static_cast of the form static_cast(e) if the declaration “T t(e);” is well-formed, for some invented temporary variable t (8.5)."

En su código está intentando static_cast por 'T = B *' y 'e = A *'

Ahora 'B * t (a *)' no está bien formado en C++ (pero 'A * t (B *)' se debe a que 'A' es una base virtual inequívoca y accesible de 'B'. Por lo tanto, el código da error.

+0

Presupuesto erróneo. – curiousguy

1

No sé si esto es "seguro", pero .

Suponiendo

B derivada de A (A pura y virtual)

ya que sé que un puntero a B sigue siendo un puntero a B.

class A 
    { 
      virtual void doSomething(const void* p) const =0; 
    }; 

    class B 
    { 
    public: 
      int value; 
      virtual void doSomething(const void*p)const 
      { 
      const B * other = reinterpret_cast<const B*>(p); 
      cout<<"hello!"<< other->value <<endl; 
      } 
    }; 

    int main() 
    { 
      B foo(1),bar(2); 
      A * p = &foo, q=&bar; 
      p->doSomething(q); 
      return 0; 
    } 

este programa se ejecuta y devuelve correctamente la impresión de "¡Hola!" y el valor del otro objeto (en este caso "2"). Por cierto, lo que estoy haciendo es muy inseguro (personalmente doy una identificación diferente a cada clase y afirmo después de reinterpretar que la ID actual es igual a otra ID para estar seguro de que estamos haciendo algo con 2 iguales clases) y como ve, me limité a métodos "const". Por lo tanto, esto funcionará con métodos "no const", pero si haces algo mal, la captura del error será casi imposible. E incluso con una afirmación, hay una probabilidad de 4 billones de tener éxito con la afirmación incluso cuando se supone que falla (assert (ID == other-> ID);)

Por cierto ... Un buen diseño de OO debería No es necesario este tipo de cosas, pero en mi caso traté de refactorizar/rediseñar el código sin poder dejar de utilizar el reinterpreteo. en general, puedes evitar este tipo de cosas.

+0

¿Estás seguro de que esto es necesario? –

+0

que es específico para su problema. un rediseño debería evitar esto en la mayoría de los casos (evite mi ejemplo si puede). oh querido, me olvido de un "const". – GameDeveloper

+0

Es decir, ¿estás seguro de que lo necesitas? ¿Qué pasa si doSomething un puntero int? Eso fallaría desastrosamente. ¿Por qué no usar un yeso dinámico y comprobar el resultado? No sé cuáles son tus requisitos exactos, pero creo que si introduces el sistema de polimorfismo estático (por ejemplo, el patrón CRTP) puedes encontrar algo más seguro. –

Cuestiones relacionadas