2010-02-09 15 views
7

tengo el siguiente código:El uso de la función virtual en los niños después de la fundición operación en C++

class A 
{ 
}; 

class B : public A 
{ 
    public: 
     virtual void f() {} 
}; 

int main() 
{ 
    A* a = new A(); 
    B* b = static_cast<B*>(a); 
    b->f(); 
} 

Este programa falla con un error de segmentación. Hay dos soluciones para hacer de este programa de trabajo:

  1. declaran f no virtual
  2. no llame b-> f() (es decir, no falla debido al reparto)

Sin embargo , ambos no son una opción. Supongo que esto no funciona debido a una búsqueda en el vtable.

(En el programa real, una tenga también funciones virtuales. Además, la función virtual no se llama en el constructor.)

¿Hay una manera de hacer este programa?

+1

+1 para una primera pregunta en el formato adecuado. –

+0

En este caso, es posible que prefiera dynamic_cast, que "fallará" (return null) con A * a = new A(), pero tendrá éxito con A * a = new B(). Deberá probar que b no es nulo antes de llamar -> f(). – jmanning2k

Respuesta

12

No se puede hacer eso porque el objeto se crea es A, no B. El yeso es un objeto de invalid-- A (creado con la nueva) no puede mágicamente convertido en un objeto de B.

Te ha significa que A * a = nuevo A() realmente es A * a = nuevo B()? En ese caso, espero que funcione.

+0

Gracias (todos) por su respuesta. Me pregunto acerca de esto: ¿Por qué funciona este programa si omito la palabra clave virtual? (El resto del código permanece sin cambios.) Intenté poner alguna funcionalidad en f() (un simple cout) y se ejecuta. Si no es un objeto de tipo B, esto no debería ser posible. ¿Derecha? – Alexander

+2

Esta podría ser su propia pregunta, pero la respuesta es que una llamada a función miembro (no virtual) 'b-> f()' es internamente idéntica a 'B :: f (b)'. En otras palabras, hay una sola función llamada 'B :: f' que toma un primer argumento implícito que es un puntero al objeto al que se llama (ese argumento es el puntero' this', y se usa para acceder a los miembros del objeto en cuestión). Con funciones virtuales, no se sabe de antemano si se debe llamar a 'B :: f' o quizás a' C :: f' y, por lo tanto, se requiere una tabla de búsqueda, que utiliza el puntero 'b' y es donde viene su segfault de. –

+1

(continúa, debido a restricciones de espacio) Entonces, con una función no virtual, la llamada 'b-> f()' (aka 'B :: f (b)') en realidad nunca * usa * el 'b 'puntero, a menos que la función' B :: f' intente acceder a los datos contenidos en el objeto 'b'. Si el cuerpo 'B :: f' es simplemente una declaración' cout', no habrá ningún problema aparente. Si, por ejemplo, define un miembro entero dentro de la clase 'B' e intenta imprimirlo en' B :: f', es probable que obtenga un segfault, o un extraño valor inesperado para ese entero. –

2

Con el fin de hacer un static_cast, usted debe estar seguro de que el objeto puede ser fundido, es decir, es un objeto de clase B.

En este caso, estoy seguro de que no es.

3

No puede hacer eso.

En su ejemplo, a es un objeto de la clase A. No B. fundición a B no significa que sea un B.

Si desea utilizar los comportamientos de objetos polimórficos, entonces se puede dar función virtual f a la clase a, y se puede usar un código como A* a = new B(); continuación, puede utilizar las funciones virtuales a través del un puntero para obtener el comportamiento de la clase B.

3

En su código:

A* a = new A(); 

se instancia un objeto A. A continuación, intenta utilizar static_cast para pasar de un tipo base de un tipo derivado:

B* b = static_cast<B*>(a); 

Si el valor en a señaló a un objeto que en realidad era de tipo B, esto sería legal y bien formada. Pero a no apunta a un objeto del tipo B, apunta a un A, por lo que el elenco evoca un comportamiento indefinido.

La solución consiste en cambiar la forma de crear una instancia del objeto. Cambio:

A* a = new A(); 

... a:

A* a = new B(); 
Cuestiones relacionadas