195 votes

Comment déclarer une classe interne ?

J'ai une classe comme ça...

class Container {
public:
    class Iterator {
        ...
    };

    ...
};

Ailleurs, je veux passer un Container::Iterator par référence, mais je ne veux pas inclure le fichier d'en-tête. Si j'essaie de déclarer la classe en avant, j'obtiens des erreurs de compilation.

class Container::Iterator;

class Foo {
    void Read(Container::Iterator& it);
};

La compilation du code ci-dessus donne...

test.h:3: error: ‘Iterator’ in class ‘Container’ does not name a type
test.h:5: error: variable or field ‘Foo’ declared void
test.h:5: error: incomplete type ‘Container’ used in nested name specifier
test.h:5: error: ‘it’ was not declared in this scope

Comment puis-je déclarer cette classe de façon à ne pas avoir à inclure le fichier d'en-tête qui déclare la classe Iterator ?

174voto

JaredPar Points 333733

Ce n'est tout simplement pas possible. Vous ne pouvez pas déclarer à l'avance une structure imbriquée en dehors du conteneur. Vous pouvez uniquement la déclarer à l'intérieur du conteneur.

Vous devez effectuer l'une des opérations suivantes

  • Rendre la classe non imbriquée
  • Modifiez l'ordre de déclaration de manière à ce que la classe imbriquée soit entièrement définie en premier.
  • Créez une classe de base commune qui peut être à la fois utilisée dans la fonction et implémentée par la classe imbriquée.

2 votes

La classe de base commune est la solution la plus utilisée de mon côté.

0 votes

Vous pouvez utiliser friend pour contourner ce problème, si vous le souhaitez.

6 votes

27voto

Todd Gardner Points 8688

Je ne crois pas que le fait de déclarer inner class of sur une classe incomplète fonctionne (parce que sans la définition de la classe, il n'y a aucun moyen de savoir s'il y a effectivement est une classe interne). Vous devrez donc inclure la définition de Container, avec une classe interne déclarée en avant :

class Container {
public:
    class Iterator;
};

Puis, dans un en-tête séparé, implémentez Container::Iterator :

class Container::Iterator {
};

Ensuite, #incluez uniquement l'en-tête du conteneur (ou ne vous souciez pas de la déclaration préalable et incluez simplement les deux).

26 votes

Bonne réponse, sauf la partie de la parenthèse du premier paragraphe. Le "il n'y a aucun moyen de savoir s'il existe réellement une classe interne" n'a pas de sens dans ce contexte et il est douteux qu'il soit exact. L'intérêt d'une déclaration forward est de dire au compilateur qu'il existe une classe (ou dans ce cas, une classe interne). Cette déclaration spécifique de votre part serait tout aussi vraie pour les classes normales et signifierait que vous ne pouvez pas déclarer quoi que ce soit.

3voto

Faisal Vali Points 10048

Je ne connais aucun moyen de faire exactement ce que vous voulez, mais voici une solution de rechange, si vous êtes prêt à utiliser des modèles :

// Foo.h  
struct Foo
{
   export template<class T> void Read(T it);
};

// Foo.cpp
#include "Foo.h"
#include "Container.h"
/*
struct Container
{
    struct Inner { };
};
*/
export template<> 
  void Foo::Read<Container::Inner>(Container::Inner& it)
{

}

#include "Foo.h"
int main()
{
  Foo f;
  Container::Inner i;
  f.Read(i);  // ok
  f.Read(3);  // error
}

Avec un peu de chance, cet idiome pourrait vous être utile (et avec un peu de chance, votre compilateur est basé sur EDG et implémente l'exportation ;) ).

-1voto

Vous ne pouvez pas le faire. Et toute cette histoire d'"éviter d'inclure les fichiers d'en-tête à tout prix" devient incontrôlable. Pour 99% des projets, je ne pense pas que l'inclusion des en-têtes nécessaires fasse une différence notable dans les temps de compilation ou les problèmes de gestion des dépendances.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X