"Tipo incompleto" en clase que tiene un miembro del mismo tipo de la propia clase


Tengo una clase que debería 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 el tipo incompleto si uso un puntero, pero prefiero no usar un puntero. Cualquier ayuda es apreciada

Author: nbro, 2011-06-15

8 answers

En el momento de declarar su miembro, todavía está definiendo la clase A, por lo que el tipo A todavía no está definido.

Sin embargo, cuando escribes A*, el compilador ya sabe que A significa un nombre de clase, por lo que el tipo "puntero a A" es definido. Es por eso que puede incrustar un puntero al tipo que está definiendo.

La misma lógica se aplica también para otros tipos, así que si solo escribes:

class Foo;

Declaras la clase Foo, pero nunca lo defina. Puedes escribir:

Foo* foo;

Pero no:

Foo foo;

Por otro lado, ¿qué estructura de memoria esperaría para su tipo A si el compilador permitiera 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 suele usar 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;
};
 37
Author: ereOn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-14 20:49:22

Este es un ejemplo práctico de lo que estás tratando de lograr:

class A {
public:
    A() : a(new A()) {}
    ~A() { delete a; a = nullptr; }
private:
    A* a;
};

A a;

Feliz Desbordamiento de Pila!

 23
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-23 07:48:51

A es "incompleta" 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 termine, no hay manera de saber qué tan grande es A (que depende de la suma de tamaños de miembros, más algunas otras cosas). Su código es un gran ejemplo de eso: su tipo A se define por el tamaño del tipo A.

Claramente, un objeto de tipo A no puede contener un objeto miembro que también sea de escriba A.

Tendrá que almacenar un puntero o una referencia; querer almacenar cualquiera de los dos es posiblemente sospechoso.

 5
Author: Lightness Races in Orbit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-14 20:47:30

No puedes incluir A dentro de A. Si pudieras hacer eso, y declararas, por ejemplo, A a;, tendrías que referirte a a.member.member.member... infinitamente. No tienes tanta RAM disponible.

 2
Author: mah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-14 20:42:51

¿Cómo puede una instancia de class A contener también otra instancia de class A?

Puede contener un puntero a A si lo desea.

 1
Author: Andrei,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-14 20:44:05

Este tipo de error ocurre cuando intenta usar una clase que aún no ha sido completamente definida.

Intente usar A* member en su lugar.

 1
Author: trema,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-14 21:11:34

El problema ocurre cuando el compilador se encuentra con un objeto de A en código. El compilador se frota la mano y se propone hacer un objeto de A. Mientras hace eso verá que A tiene un miembro que es de nuevo de tipo A. Así que para completar la instanciación de A ahora tiene que instanciar otra A, y al hacerlo tiene que instanciar otra 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 conocer todos los tipos y memoria requisito de todos los miembros antes de que comience a instanciar un objeto de una clase.

 0
Author: Sushovan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-10 10:28:17

Una forma sencilla de entender la razón por la que la clase A está incompleta es intentar mirarla desde la perspectiva del compilador.

Entre otras cosas, el compilador debe ser capaz de 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 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 conduce a la recursión infinita.

La forma del compilador de tratar este problema es considerar A incompleto hasta que su definición sea completamente conocida. Se le permite declarar punteros y referencias a clase incompleta, pero no se le permite declarar valores.

 0
Author: dasblinkenlight,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-31 14:00:12