2010-10-25 19 views
15

Los hechos:dependencia circular en C++

  • Tengo dos clases predominantes: Director y Especialista.
  • Existen varios tipos diferentes de especialistas.
  • Los especialistas a menudo requieren la ayuda de otros especialistas para realizar su trabajo.
  • El gerente conoce a todos los especialistas e inicialmente, cada especialista solo conoce a su gerente. (Este es el problema.)
  • En tiempo de ejecución, el administrador crea y almacena una lista de especialistas. A continuación, el administrador recorre la lista y le pide a cada especialista que se inicialice. Durante su inicialización, cada especialista solicita al gerente que les proporcione otros especialistas que cumplan con alguna descripción. Una vez que esto se completa, el Administrador entra en un ciclo durante el cual se les pide a los Especialistas que realicen su tarea especializada.

Para mí parece que este es un patrón decente, pero como un Gerente tiene una lista de Especialistas y un Especialista tiene un Gerente, tengo problemas de dependencia circular.

¿Es este un caso en el que de alguna manera debo adelantar declarar la existencia de una clase de otra? (De ser así, ¿cómo?) ¿O debería usar algún patrón de diseño para solucionar este problema? (Si es así, ¿qué?) Además ... pensé que el patrón en sí era bastante bueno. así que no me importaría que alguien me ayude a entender por qué esto es malo.

+0

¿Podría mostrarnos una muestra de lo que tiene y exactamente qué problemas tiene? –

+0

He visto esta pregunta muy similar varias veces hace poco - aquí está la más reciente http://stackoverflow.com/questions/4016471/circular-reference-problem –

+0

@Greg - Las preguntas son similares, sin embargo, yo Estoy interesado no solo en resolver la dependencia circular, sino también en entender si el patrón que uso está defectuoso por algún motivo. – JnBrymn

Respuesta

23

En ambos casos, declarar delante de la otra clase:

Manager.h

class Specialist; 

class Manager 
{ 
    std::list<Specialist*> m_specialists; 
}; 

especialista.h

class Manager; 

class Specialist 
{ 
    Manager* m_myManager; 
}; 

La única vez que necesita para llevar en el archivo de cabecera para una clase es cuando se necesita utilizar una función miembro o variable dentro de esa clase, o la necesidad de utilizar la clase como un tipo de valor, etc. Cuando solo necesita un puntero o una referencia a una clase, una declaración directa será suficiente.

Tenga en cuenta que las declaraciones directas no son solo para resolver dependencias circulares. Debe usar declaraciones directas siempre que sea posible. Son siempre preferible a incluir un archivo de encabezado extra si es viable.

+4

1 - "declaración adelantada" son las palabras clave – leonbloy

+4

"Ellos son siempre preferibles" Me _strongly_ de acuerdo y sostienen que son preferibles _rarely_ dondequiera que se pueden evitar. El uso de declaraciones avanzadas en un formato grande hace que el código sea más difícil de comprender porque resulta más difícil rastrear las dependencias. Además, a menudo hay poca o ninguna ganancia de rendimiento cuando se utiliza un compilador moderno que almacena en caché los archivos de forma agresiva y admite encabezados precompilados. [En una nota relacionada, ¿por qué sugieres 'std :: list'?] –

+3

localizar a los cuales dependencias? Si una declaración directa funciona, entonces ya no existe una dependencia. Además, es tan fácil seguir un rastro de clase como un rastro de encabezado en cualquier IDE a mitad de camino. En cuanto a usar 'std :: list', dice en el PO que el gestor almacena una lista de especialistas, por lo que decidió interpretar que literalmente. Probablemente usaré un 'vector', pero por supuesto depende de los casos de uso particulares. –

1

Una opción es reenviar declarar una de las personas, como usted sugiere:

struct specialist; 

struct manager 
{ 
    std::vector<std::shared_ptr<specialist> > subordinates_; 
}; 

struct specialist 
{ 
    std::weak_ptr<manager> boss_; 
}; 

Sin embargo, si al final tener más de una estructura de árbol (donde tiene varias capas de gestión, un person clase base también funcionaría:

struct person 
{ 
    virtual ~person() { } 
    std::weak_ptr<person> boss_; 
    std::vector<std::shared_ptr<person> > subordinates_; 
}; 

a continuación, puede derivar clases específicas para los diferentes tipos de personas en la jerarquía Aun cuando no se necesita esto depende de cómo exactamente se va a utilizar las clases

..

Si su implementación no es compatible con std::shared_ptr, puede admitir std::tr1::shared_ptr o puede usar boost::shared_ptr.

+0

en este modelo tienes que asegurar que todos los punteros 'manager' se envuelven en' shared_ptr'? De lo contrario, no hay forma de validar el 'weak_ptr' ¿está allí? Curioso acerca de esto, ya que viene mucho por mí. –

+0

@Steve: Sí. Es mucho más fácil si solo tienes un tipo de clase en la jerarquía (como 'persona 'en mi segundo ejemplo). –

1

esto es algo normal. Sólo se necesita

class Manager; 

en la cabecera y especialista

class Specialist; 

en la cabecera del gestor de

si está utilizando shared_ptrs es posible encontrar shared_from_this útiles. (No para recorrer sino porque parece que usted lo necesitará de todos modos)

8

Es una cuestión de gusto, pero forward declaration a menudo es una buena alternativa para incluir en archivos de encabezado incluso sin dependencias circulares. (No quiero plantear una discusión sobre que en este lugar.) Así pues, aquí es un ejemplo de cómo aplicar las declaraciones hacia adelante para su problema:

En Manager.h:

// Forward declaration: 
class Specialist; 

// Class declaration: 
class Manager 
{ 
    // Manager declarations go here. 
    // Only pointers or references to 
    // the Specialist class are used. 
}; 

En Manager.cpp:

#include "Specialist.h" 

// Manager definitions/implementations 
// using the Specialist class go here. 
// Full Specialist functionality can be used. 

En Specialist.h:

// Forward declaration: 
class Manager; 

// Class declaration: 
class Specialist 
{ 
    // Specialist declarations go here. 
    // Only pointers or references to 
    // the Manager class are used. 
}; 

En Specialist.cpp:

#include "Manager.h" 

// Specialist definitions/implementations 
// using the Manager class go here. 
// Full Manager functionality can be used. 
+0

Gracias por el ejemplo explícito, ¡esa fue la única respuesta útil hasta el momento! – fuenfundachtzig

1

Mientras todos los demás están respondiendo la pregunta principal, pensé en señalarlo.

En tiempo de ejecución, el administrador crea y almacena una lista de especialistas. A continuación, el administrador recorre la lista y le pide a cada especialista que se inicialice. Durante su inicialización, cada especialista solicita al gerente que les proporcione otros especialistas que cumplan con alguna descripción. Una vez que esto se completa, el Administrador entra en un ciclo durante el cual se les pide a los Especialistas que realicen su tarea especializada.

Solo quiero señalar que esto tiene que ser un proceso de dos pasos. ¿Cómo puede el gerente decirle al especialista 1 qué especialistas existen para la tarea B si el gerente solo conoce a un especialista hasta el momento? Entonces usted necesita:

1) El gerente revisa la lista de especialistas y les pide que se identifiquen.

2) El administrador revisa la lista de especialistas y les pregunta a qué especialidades necesitan acceder, diciéndoles quién puede cumplir con sus requisitos.

3) el administrador hace una lista de especialistas y les dice que realicen sus acciones.

Cuestiones relacionadas