2009-11-17 17 views
5

Estoy tratando de implementar un ejemplo de patrón de visitante, pero tengo problemas con las dependencias circulares de las declaraciones de clases. Cuando hago la declaración adelante de clase Visitante, las clases Rusia e Inglaterra no saben que el Visitante tiene la visita del método, pero cuando extiendo la declaración adelante del Visitante para la aceptación del método, necesito usar clases Inglaterra y Rusia, pero necesitan saber quién El visitante lo está, porque están usando este tipo en su código. Probé muchas variaciones para ordenar el código, pero fallé por completo. Por favor, ayúdame a entender lo que C++ necesita para conseguir esto. Gracias.Dependencias circulares de las declaraciones

 
#include <cstdio> 
#include <vector> 

using namespace std; 

class Visitor; 

class Land { 
    public: 
    virtual void accept(const Visitor *v); 
}; 

class England : public Land { 
    public: 
    void accept(const Visitor *v) { 
     v->visit(this); 
    } 
}; 

class Russia : public Land { 
    public: 
    void accept(const Visitor *v) { 
     v->visit(this); 
    } 
}; 

class Visitor { 
    public: 
    void visit(const England *e) const { 
     printf("Hey, it's England!\n"); 
    } 

    void visit(const Russia *r) const { 
     printf("Hey, it's Russia!\n"); 
    } 
}; 

class Trip { 
    private: 
    vector<Land> *l; 
    public: 
    explicit Trip(vector<Land> *_l):l(_l) {} 
    void accept(Visitor *v) { 
     for (unsigned i = 0; i < l->size(); i++) { 
     l->at(i).accept(v); 
     } 
    } 
}; 

int main() { 
    England england; 
    Russia russia; 
    vector<Land> trip_plan; 
    trip_plan.push_back(england); 
    trip_plan.push_back(russia); 
    trip_plan.push_back(england); 
    Trip my_trip(&trip_plan); 
    Visitor me; 
    my_trip.accept(&me); 
    return 0; 
} 

Y no es la salida g ++

 
c++ -ansi -Wall -Wextra -Wconversion -pedantic -Wno-unused-parameter -o vp vp.cc 
vp.cc: In member function ‘virtual void England::accept(const Visitor*)’: 
vp.cc:40: error: invalid use of incomplete type ‘const struct Visitor’ 
vp.cc:30: error: forward declaration of ‘const struct Visitor’ 
vp.cc: In member function ‘virtual void Russia::accept(const Visitor*)’: 
vp.cc:47: error: invalid use of incomplete type ‘const struct Visitor’ 
vp.cc:30: error: forward declaration of ‘const struct Visitor’ 

Respuesta

8
class Visitor; 

class England : public Land { 
    public: 
    void accept(const Visitor *v); // Only declaration 
}; 


// Define Visitor 
class Visitor { 
    //... 
}; 

// Now implementation 
void England::accept(const Visitor *v) { 
     v->visit(this); 
} 
+0

Tengo miedo de que esto no resuelva el problema. Ahora obtengo mucha: referencia indefinida a 'vtable for Land ', y referencia indefinida a' typeinfo for Land'. – Martin

+0

¿De hecho has definido la clase Land en cualquier lugar? Creo que probablemente ha querido hacer que la Tierra sea puramente virtual: clase Tierra { public: virtual ~ Land() {} virtual void accept (const Visitor * v) = 0; }; El error que está viendo es que GCC no ha creado el vtable para Land porque nunca definió funciones para él. –

+0

Bueno, Alexey resolvió este problema, pero aparece un nuevo problema: http://stackoverflow.com/questions/1748827/virtual-tables-are-undefined – Martin

0

tengo no escribe complejo programa en C++ en mucho tiempo, pero si no recuerdo mal, usted debe salir el esqueleto de esas clases en .h archivo con el mismo nombre con este archivo .c. Luego inclúyalo en este archivo .c.

Espero que esto ayude.

+0

Bueno, no quiero "resolver" el problema. Quiero entender qué está mal y por qué. Pero gracias. – Martin

0

Dar toda la declaración de tipo de clase antes de su uso ... creo que funcionaría.

1

Alexy ya dio una parte de la respuesta.

Sin embargo, si usted no va a aplicar para aceptar la tierra, entonces usted necesita: la respuesta de

class Land { 
    public: 
    virtual void accept(const Visitor *v)= NULL; 
}; 
+0

Este problema ha sido separado de la pregunta original: http://stackoverflow.com/questions/1748827/virtual-tables-are-undefined – Martin

1

Alexey Malistov qué a resolver su problema. Simplemente también expone el siguiente problema.

El compilador gcc se queja del vtable (que se usa para clases con funciones virtuales, entre otras cosas). Las reglas que utiliza están documentados (ver docs):

Si la clase declara que no sean del inline, funciones virtuales no puros, el primero de ellos es elegido como el "método de clave" para la clase, y la viable solo se emite en la unidad de traducción donde se define el método clave.

Ahora, la versión de Alexey de su clase define la no-inline, función virtual pura no acepta . Entonces, gcc aplaza la creación de instancias del Land vtable hasta que vea la definición de Land :: accept. Agregue eso y vea si funciona. O, como dice Nicholaz, simplemente haz que sea puramente virtual.

Bueno, no quiero a "resolver" el problema. Quiero entender lo que está mal y por qué

Hay que acostumbrarse a que separa las declaraciones de las definiciones. Excepto por el caso especial de plantillas, C++ tiende a funcionar mejor de esta manera.

1

Cuando reenvía la declaración, el compilador de C++ sabe que hay un tipo definido por el usuario de este tipo, pero que desconoce sus miembros y métodos de datos.Para usar la función completa de este tipo definido por el usuario, debe incluir su archivo de encabezado donde se encuentran todos sus métodos y miembros de datos antes de usarlos. De lo contrario, solo haga la declaración directa y use sus métodos y miembros de datos donde está el archivo de encabezado incluido. En su caso está llamando al método visit() de la clase Visitor Visitor declarada, de esta manera le informa al compilador que hay un tipo de datos Visitor, pero el compilador aún no conoce el método visit(). Para resolver esto, debe eliminar la declaración de reenvío y poner la definición de Visitante en la parte superior de todas las clases. Tendrás algo como esto

#include <cstdio> 
#include <vector> 

using namespace std; 
class England; 
class Russia; 
class Visitor { 
    public: 
    void visit(const England *e) const { 
     printf("Hey, it's England!\n"); 
    } 

    void visit(const Russia *r) const { 
     printf("Hey, it's Russia!\n"); 
    } 
}; 

class Land { 
    public: 
    virtual void accept(const Visitor *v); 
}; 

class England : public Land { 
    public: 
    void accept(const Visitor *v) { 
     v->visit(this); 
    } 
}; 

class Russia : public Land { 
    public: 
    void accept(const Visitor *v) { 
     v->visit(this); 
    } 
}; 
... 
+0

¡Gracias por la actualización! – Martin

Cuestiones relacionadas