c++ - sociales - qué es clase para sí
"Tipo incompleto" en clase que tiene un miembro del mismo tipo de la clase en sí (8)
Tengo una clase que debe tener un miembro privado de la misma clase, algo así como:
class A {
private:
A member;
}
Pero me dice que el miembro es un tipo incompleto. ¿Por qué? No me dice tipo incompleto si uso un puntero, pero prefiero no usar un puntero. Cualquier ayuda es apreciada
¿Cómo puede una instancia de la class A
también contener otra instancia de la class A
?
Puede mantener un puntero a A si lo desea.
El problema ocurre cuando el compilador encuentra un objeto de A en el código. El compilador frotará su mano y establecerá hacer un objeto de A. Mientras lo hace, verá que A tiene un miembro que también es de tipo A. Entonces, para completar la creación de instancias de A, ahora tiene que crear una instancia de otra A, y en al hacerlo, tiene que crear una instancia de A y así sucesivamente. Puedes ver que terminará en una recursión sin límite. Por lo tanto, esto no está permitido. El compilador se asegura de que conoce todos los tipos y los requisitos de memoria de todos los miembros antes de comenzar a crear instancias de un objeto de una clase.
En el momento en que declaras tu miembro, sigues definiendo la clase A
, por lo que el tipo A
aún no está definido.
Sin embargo, cuando escribe A*
, el compilador ya sabe que A
representa un nombre de clase, por lo que se define el tipo "puntero a A". Es por eso que puedes insertar un puntero al tipo que estás definiendo.
La misma lógica se aplica también para otros tipos, así que si solo escribe:
class Foo;
Usted declara la clase Foo, pero nunca la define. Puedes escribir:
Foo* foo;
Pero no:
Foo foo;
Por otro lado, ¿qué estructura de memoria esperarías para tu tipo A
si el compilador permitía una definición recursiva?
Sin embargo, a veces es lógicamente válido tener un tipo que de alguna manera se refiera a otra instancia del mismo tipo. La gente generalmente usa punteros para eso o incluso mejor: punteros inteligentes (como boost::shared_ptr
) para evitar tener que lidiar con la eliminación manual.
Algo como:
class A
{
private:
boost::shared_ptr<A> member;
};
Este es un ejemplo de trabajo de lo que intenta lograr:
class A {
public:
A() : a(new A()) {}
~A() { delete a; a = nullptr; }
private:
A* a;
};
A a;
Happy !
Este tipo de error ocurre cuando intenta utilizar una clase que aún no ha sido completamente DEFINIDA.
Intenta usar el A* member
lugar.
No puede incluir A en A. Si pudo hacerlo, y declaró, por ejemplo, A a;
, necesitaría referirse a a.member.member.member...
infinitamente. No tienes mucha RAM disponible.
Una manera simple de entender que la razón detrás de la clase A
es incompleta es tratar de verla desde la perspectiva del compilador.
Entre otras cosas, el compilador debe poder calcular el tamaño del objeto A
Conocer el tamaño es un requisito muy básico que aparece en muchos contextos, como asignar espacio en la memoria automática, llamar al operador new
y evaluar el sizeof(A)
. Sin embargo, calcular el tamaño de A
requiere conocer el tamaño de A
, porque a
es un miembro de A
Esto lleva a la recursión infinita.
La forma en que el compilador maneja este problema es considerar que A
incompleto hasta que su definición sea completamente conocida. Se le permite declarar punteros y referencias a clases incompletas, pero no se le permite declarar valores.
A
está "incompleto" hasta el final de su definición (aunque esto no incluye los cuerpos de las funciones miembro).
Una de las razones para esto es que, hasta que la definición finalice, no hay forma de saber qué tan grande es A
(que depende de la suma de los tamaños de los miembros, más algunas otras cosas). Su código es un gran ejemplo de eso: su tipo A
está definido por el tamaño del tipo A
Claramente, un objeto de tipo A
no puede contener un objeto miembro que también sea de tipo A
Tendrás que guardar un puntero o una referencia; querer almacenar cualquiera de ellos es posiblemente sospechoso.