patterns gof examples book c++ design-patterns

c++ - gof - design patterns singleton



Dependencias circulares de declaraciones (6)

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.

#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 está la salida de 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’


Alexy ya dio una parte de la respuesta.

Sin embargo, si no va a implementar accept para Land, entonces necesita:

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


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


La respuesta de Alexey Malistov resuelve tu 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 usa están documentadas (ver documentos ):

Si la clase declara funciones virtuales no puras, no en línea, se elige la primera como el "método clave" para la clase, y la variable vtable solo se emite en la unidad de traducción donde se define el método clave.

Ahora, la versión de su clase de Alexey define la aceptación de la función virtual no pura y no en línea. Entonces, gcc aplaza la instanciación 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 "resolver" el problema. Quiero entender qué está mal y por qué

Acostúmbrate a separar las declaraciones de las definiciones. Excepto por el caso especial de plantillas, C ++ tiende a funcionar mejor de esta manera.


No escribí un programa complejo de C ++ en mucho tiempo, pero si no recuerdo mal, debería eliminar el esqueleto de esas clases en el archivo .h con el mismo nombre con este archivo .c . Luego .c en este archivo .c .

Espero que esto ayude.


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); }


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 forma 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); } }; ...