Todas las soluciones para circular incluyen dependencias que he visto simplemente decir en "este caso particular" la definición de clase completa no es necesaria ya que "usted" solo está usando punteros a esa clase.

Encontré este problema y lo solucioné usando declaraciones hacia adelante.

Me pregunto qué se supone que debe hacer cuando necesita la definición específica de la otra clase en ambas clases.

Además, ¿por qué el uso de un puntero a la clase le permite usar una declaración directa en lugar de una definición de clase?

c++
0
Spectral 1 sep. 2017 a las 06:08

2 respuestas

La mejor respuesta

¿En qué casos necesitaría conocer la especificación de antemano para ambas clases?

Un caso imposible es el siguiente:

class A
{
    B m_b;
};

class B
{
    A m_a;
};

Pero esto es imposible ya que el tamaño de la clase A depende del tamaño de la clase B, pero el tamaño de la clase B depende del tamaño de la clase A. También obtendrá una serie infinita A myA; myA.m_b.m_a.m_b.m_a.... cuando intente construir ya sea.

Si usa punteros, no necesita saber el tamaño de ninguno; un puntero es siempre del mismo tamaño dependiendo de la plataforma en la que se encuentre. Y la serie desaparece porque los objetos del montón deben crearse explícitamente.

2
jakedipity 1 sep. 2017 a las 03:16

Me pregunto qué se supone que debe hacer cuando necesita la definición específica de la otra clase en ambas clases.

Se puede hacer con declaraciones hacia adelante y definiciones diferidas en compiladores modernos . Muchos compiladores antiguos solo permiten punteros y referencias para reenviar tipos declarados.

Aquí hay un ejemplo artificial:

A.hpp

class B;

class A
{
public:
    int32_t Value;

    A(int32_t value) : Value(value) { }

    int32_t Add(B b) const;
}

B.hpp

#include "A.hpp"

class B
{
public:
    int32_t Value;

    B(int32_t value) : Value(value) { }

    int32_t Sub(A a) const;
}

AB.hpp

#include "A.hpp"
#include "B.hpp"

inline int32_t A::Add(B b) const
{
    return this->Value + b.Value;
}

inline int32_t B::Sub(A a) const
{
    return this->Value - a.Value;
}

Además, ¿por qué el uso de un puntero a la clase le permite usar una declaración directa en lugar de una definición de clase?

Las declaraciones de reenvío son solo nombres para el compilador. El concepto existe para que pueda utilizar tipos que no se han definido todavía . Esto es necesario debido a la forma en que C ++ analiza el código, un artefacto del lenguaje C del que hereda mucho. Los analizadores de C ++ son en realidad procesadores de texto de sólo avance que inyectan texto cuando #include y usa macros. Es un modelo conceptualmente simple que hizo que los compiladores de C / C ++ fueran más fáciles de escribir en los primeros días. Compare esto con C # / Java, donde solo usa using / import y felizmente crea dependencias circulares entre clases con una sintaxis simple.

Los punteros son realmente números enteros, similares a short y int, pero con una semántica especial impuesta por el lenguaje y un tamaño fijo conocido en tiempo de compilación basado en la arquitectura de la CPU. Esto hace que las declaraciones de punteros sean muy simples de manejar para los compiladores.

La declaración hacia adelante facilita las dependencias circulares y la ocultación de la implementación (lo que también acelera el tiempo de compilación). Considere el idioma pimpl. Sin declaraciones de reenvío, no existe una forma segura de ocultar los detalles de implementación.

0
Koby Duck 1 sep. 2017 a las 07:15