web-dev-qa-db-fra.com

"Type incomplet" dans la classe qui a un membre du même type que la classe elle-même

J'ai une classe qui devrait avoir un membre privé de la même classe, quelque chose comme:

class A {
    private:
        A member;
}

Mais il me dit que ce membre est un type incomplet. Pourquoi? Cela ne me dit pas que le type est incomplet si j'utilise un pointeur, mais je préfère ne pas utiliser de pointeur Toute aide est appréciée

34
Sterling

Au moment où vous déclarez votre membre, vous êtes toujours définissant la classe A; le type A n'est donc pas défini.

Cependant, lorsque vous écrivez A*, le compilateur sait déjà que A correspond à un nom de classe et que le type "pointeur sur A" est défini. C'est pourquoi vous pouvez intégrer un pointeur au type que vous définissez.

La même logique s'applique également aux autres types, donc si vous écrivez simplement:

class Foo;

Vous déclarez la classe Foo, mais vous ne la définissez jamais. Tu peux écrire:

Foo* foo;

Mais non:

Foo foo;

D'autre part, quelle structure de mémoire attendriez-vous pour votre type A si le compilateur permettait une définition récursive?

Cependant, il est parfois logiquement correct d'avoir un type faisant référence à une autre instance du même type. Les gens utilisent généralement des pointeurs pour cela ou même mieux: des pointeurs intelligents (tels que boost::shared_ptr) pour éviter de devoir traiter avec une suppression manuelle.

Quelque chose comme:

class A
{
  private:
    boost::shared_ptr<A> member;
};
38
ereOn

Voici un exemple concret de ce que vous essayez d’atteindre:

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

A a;

Happy Stack Overflow!

25
Nick

A est "incomplet" jusqu'à la fin de sa définition (bien que cela n'inclue pas les corps des fonctions membres).

L’une des raisons est que, jusqu’à ce que la définition prenne fin, il n’ya aucun moyen de savoir quelle est la taille de A (qui dépend de la somme des tailles des membres, plus quelques autres choses). Votre code est un excellent exemple de cela: votre type A est défini par la taille de type A.

Il est clair qu'un objet de type A ne peut pas contenir d'objet membre également de type A.

Vous devrez stocker un pointeur ou une référence; vouloir stocker l'un ou l'autre est peut-être suspect.

Vous ne pouvez pas inclure A dans A. Si vous pouviez le faire et que vous déclariez, par exemple, A a;, vous devrez vous référer à a.member.member.member... infiniment. Vous n'avez pas beaucoup RAM disponible.

2
mah

Comment une instance de class A peut-elle aussi contenir une autre instance de class A?

Il peut contenir un pointeur sur A si vous le souhaitez.

1
Andrei

Ce type d'erreur se produit lorsque vous essayez d'utiliser une classe qui n'a pas encore été complètement définie.

Essayez d'utiliser A* member à la place.

1
trema

Un moyen simple de comprendre la raison pour laquelle la classe A est incomplète est d'essayer de l'examiner du point de vue du compilateur.

Entre autres choses, le compilateur doit pouvoir calculer la taille de l’objet A. Connaître la taille est une exigence de base qui apparaît dans de nombreux contextes, comme allouer de l’espace en mémoire automatique, appeler l'opérateur new et évaluer sizeof(A). Cependant, le calcul de la taille de A nécessite de connaître la taille de A, car a est un membre de A. Cela conduit à une récursion infinie.

La façon dont le compilateur traite ce problème est de considérer A incomplet jusqu'à ce que sa définition soit complètement connue. Vous êtes autorisé à déclarer des pointeurs et des références à une classe incomplète, mais vous n'êtes pas autorisé à déclarer des valeurs.

0
dasblinkenlight

Le problème se produit lorsque le compilateur rencontre un objet de A dans le code . Le compilateur se frotte la main et commence à créer un objet de A. En faisant cela, il verra que A a un membre qui est à nouveau de type A Donc, pour compléter l'instanciation de A, il doit maintenant instancier un autre A et, ce faisant, il doit instancier un autre A, etc. Vous pouvez voir que cela aboutira à une récursion sans limite. Par conséquent, cela n'est pas autorisé. Le compilateur s'assure de connaître tous les types et tous les besoins en mémoire de tous les membres avant d'instancier un objet d'une classe. 

0
Sushovan