2012-08-15 17 views
8

Estoy pasando por una prueba de C++. Y encontré el siguiente código: es ilegal, pero no puedo entender por qué. ¿Puede alguien explicar por qué esta línea:Herencia: ¿por qué es esto ilegal?

Box* b1 = s1->duplicate(); 

genera el error del compilador, "no se puede convertir de la forma * a Box"? Supuse que s1->duplicate() llama a Box::duplicate() porque s1 realmente apunta a un Box - pero del error del compilador parece que está llamando al Shape::duplicate().

#include <iostream> 

struct Shape 
{ 
    virtual Shape* duplicate() 
    { 
    return new Shape; 
    } 

    virtual ~Shape() {} 
}; 

struct Box : public Shape 
{ 
    virtual Box* duplicate() 
    { 
    return new Box; 
    } 

}; 

int main(int argc, char** argv) 
{ 
    Shape* s1 = new Box; 

    Box* b1 = s1->duplicate(); 

    delete s1; 
    delete b1; 
    return 0; 
} 
+1

Debido a [l su] (http://stackoverflow.com/questions/4665117/c-virtual-function-return-type). –

Respuesta

8

Shape::duplicates() devuelve un Shape*, que no es un Box*. El tipo de tiempo de ejecución que realmente devuelve no tiene nada que ver con eso. ¿Cómo podría el compilador saber que el Shape* devuelto realmente apunta a un Box?

Editar: Piense en esto:

struct Shape 
{ 
    virtual Shape* duplicate() 
    { 
    return new Shape; 
    } 

    virtual ~Shape() {} 
}; 

struct Box : public Shape 
{ 
    virtual Box* duplicate() 
    { 
    return new Box; 
    } 

}; 

struct Sphere : public Shape 
{ 
    virtual Sphere* duplicate() 
    { 
    return new Sphere; 
    } 

}; 

Shape* giveMeABoxOrASpehere() 
{ 
    if (rand() % 2) 
     return new Box; 
    else 
     return new Sphere; 
} 

// 
Shape* shape = giveMeABoxOrASphere(); 
// What does shape->duplicate() return? 

Box* shape = giveMeABoxOrASphere(); 
// shoud this compile? 
+0

Hmm. Bueno, supongo que estaba pensando en la línea, que si el compilador puede aceptar 'Shape * s' = new Box' entonces de alguna manera * sabría * que' s1' ahora es un puntero a un Box. Es tarde, creo que mi cerebro está frito. . . – BeeBand

+2

@BeeBand: Hay una diferencia importante entre 'Shape * s = new Box;' y 'Box * b = new Shape;'. – aschepler

+1

@BeeBand en este caso se puede encontrar con un 'dynamic_cast', por lo que la información del tipo no se pierde por completo. Simplemente no está disponible para el compilador. –

15

El lenguaje C++ está escrito estáticamente. Las decisiones sobre la legalidad de su llamada se toman en tiempo de compilación. El compilador, obviamente, no puede saber que s1->duplicate() devuelve un puntero a un objeto Box. En estas circunstancias, sería ilógico esperar que acepte su código.

Sí, s1->duplicate() de hecho llama Box::duplicate en su ejemplo, pero ¿cómo espera que el compilador sepa esto? Se puede decir que es "obvio" a partir de su ejemplo específico, pero la especificación de esta característica de idioma no es una excepción para tales casos "obvios".

1

Por la misma razón exacta

Shape* s1 = new Box; 
Box* b1 = s1; 

no compila. Al compilador no le importa que s1 se refiera a Box, ni debería importarle.

Si sabe que s1 se refiere a un Box, justo decirlo:

Box *s1 = new Box; 

Una nota acerca de la sintaxis: las reglas de análisis para Box * s1; son (muy simplificada):

declaration := type-name declarator ; 
declarator := name 
      | * declarator 

por lo que la El análisis es:

Box  *  s1  ; 
        ^^^^^^^^ 
        declarator 
^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ 
type-name   declarator 

y la agrupación es Box (* (s1))

Se considera mejor estilo de escribir Box *s1; porque es más consistente con el análisis de Box* s1; Si se declara más de una variable en una declaración, la sintaxis Box* puede ser confuso:

Box* x, y; 

x es un puntero a Box, pero y es un Box, ya que el análisis es:

Box (*x), y; 
+0

bien gracias por la sugerencia re. analizando. . . – BeeBand

Cuestiones relacionadas