2011-01-27 11 views
47

¿Tiene algún buen consejo sobre cómo evitar dependencias circulares de archivos de encabezado, por favor?Evitando las dependencias circulares de los archivos de encabezado

Por supuesto, desde el principio, trato de diseñar el proyecto lo más transparente posible. Sin embargo, a medida que se agregan más y más características y clases, y el proyecto se vuelve menos transparente, comienzan a ocurrir dependencias circulares.

¿Existen reglas generales, verificadas y de trabajo? Gracias.

Respuesta

48

Si tiene dependencia circular entonces haciendo algo mal.

Como por ejemplo:

foo.h 
----- 
class foo { 
public: 
    bar b; 
}; 

bar.h 
----- 
class bar { 
public: 
    foo f; 
}; 

es ilegal es probable que desee:

foo.h 
----- 
class bar; // forward declaration 
class foo { 
    ... 
    bar *b; 
    ... 
}; 

bar.h 
----- 
class foo; // forward declaration 
class bar { 
    ... 
    foo *f; 
    ... 
}; 

Y esto está bien.

reglas generales:

  1. asegurarse de que cada cabecera se puede incluir en sí mismo.
  2. ¡Si puede usar declaraciones directas, úselas!
+0

+1 Hola Artyom, gracias por la respuesta. un uso más frecuente de declaraciones directas podría ser útil. –

+0

@Artyom: si el puntero está destinado a ser el propietario del recurso, te aconsejo que uses 'scoped_ptr' o' unique_ptr'. Si el puntero es simplemente una referencia a un objeto, entonces podría ser necesario usar un patrón Observer para que esté "desarmado" cada vez que se destruya el objeto al que se hace referencia. –

+0

@Matthieu M. Por supuesto, (o 'auto_ptr' que es mejor cuando no quieres depender de' boost' o 'C++ 0x'). Pero prefiero mostrar una idea general en lugar de un código en vivo. Incluso puede ser 'std :: vector ' que funcionaría si foo tiene una declaración directa. – Artyom

4

en función de sus capacidades preprocesador:

#pragma once 

o

#ifndef MY_HEADER_H 
#define MY_HEADER_H 
your header file 
#endif 

Si le resulta muy aburrido para diseñar archivos de cabecera quizá makeheaders de Hwaci (diseñadores de SQLite y DVCS fósiles) podría ser de interés para ti

+2

Esto no es tanto evitar dependencias circulares, para evitar "redefinición del símbolo" errores. Sin embargo, es una práctica estándar y absolutamente necesaria. –

+0

Hola Benoid, sí, tengo que estar de acuerdo con Peter Torok. Esto algo se explica en cada libro de texto y en una práctica de uso obligatorio. Muchas gracias por su respuesta. –

6

Un enfoque general es factorizar las características comunes en un tercer archivo de encabezado al que los dos archivos de encabezado originales hacen referencia.

Ver también Circular Dependency Best Practice

+0

+1 Hola Ed, ese es otro muy buen consejo. Gracias. –

+0

Revisé el enlace que proporcionó y muestra un buen ejemplo de rediseño de clases para evitar dependencias circulares. –

+0

Gracias, me gustó también ;-) –

3

Lo que está destinado a es un layered approach. Puede definir capas donde los módulos pueden depender de módulos de capa inferior, pero la inversa debe hacerse con observers. Ahora todavía puede definir qué tan finas deben ser sus capas y si acepta la dependencia circular dentro de las capas, pero en este caso usaría this.

+0

+1 hola Stefaanv, el enfoque en capas es bastante nuevo para mí y parece algo que requiere muchas preparaciones y rediseños. Es un consejo muy valioso. Gracias. –

+0

El enfoque por capas es una gran idea, especialmente porque no es específico de C++ y, por lo tanto, es valioso en muchas situaciones :) –

15
  • Usar declaraciones avanzadas cuando sea posible.
  • Mueva cualquier encabezado incluido fuera de un archivo de encabezado y en el archivo cpp correspondiente si solo lo necesita el archivo cpp. La forma más fácil de hacer cumplir esto es hacer que el #include "myclass.h" sea el primero en incluir myclass.cpp.
  • La introducción de interfaces en el punto de interacción entre clases separadas puede ayudar a reducir las dependencias.
+1

+1 Hola Jon, gracias por tu respuesta. Algunos de tus consejos ya se mencionaron anteriormente, pero uno para incluir #include heade files en archivos .cpp en vez de archivos .h era nuevo y útil. –

+0

Creo que esta respuesta responde mejor a la pregunta sobre cómo evitar los errores de compilación con dependencias circulares, al tiempo que evita el mantra de que hizo algo incorrecto porque tiene que tratar con una dependencia circular. Si está trabajando con patrones de diseño GoF y complejidad, tendrá una dependencia circular en algún momento. El mejor consejo no es solo la declaración directa (que simplifica en exceso la solución), sino el punto # 2. –

+0

Segunda sugerencia es lo que estaba buscando – rluks

3

En general, los archivos de encabezado deben declarar en lugar de incluir otros encabezados cuando sea posible.

También asegúrese de que se adhieren a una clase por cabecera.

A continuación, es casi seguro que no va a ir mal.

Lo peor de acoplamiento por lo general proviene de código de la plantilla hinchado. Como tiene que incluir la definición dentro del encabezado, a menudo conduce a que se tengan que incluir todos los encabezados de clases, y luego la clase que usa la plantilla incluye el encabezado de la plantilla, que incluye una carga de otras cosas.

Por esta razón, me generalmente decimos: tener cuidado con las plantillas! Idealmente, una plantilla no debería tener que incluir nada en su código de implementación.

+0

+1 Hola CashCow, para ser honesto, no le presté mucha atención a las declaraciones futuras. En cambio, utilicé #include. Muchas gracias por esta respuesta. –

6

Algunas buenas prácticas que sigo para evitar dependencias circulares son,

  1. Se adhieren a los principios de desarrollo OO. No incluya un archivo de encabezado, a menos que la clase incluida esté en relación de composición con la clase actual. Use la declaración directa en su lugar.
  2. Diseñe clases abstractas para que actúen como interfaces para dos clases. Haga la interacción de las clases a través de esa interfaz.
+0

+1 hola Arun, especialmente el segundo consejo para usar clases abstractas/de interfaz fue útil. Voy a darle una oportunidad. Gracias. –

Cuestiones relacionadas