Séparation du code de classe en un en-tête et un fichier cpp

170

Je ne sais pas comment séparer le code d'implémentation et de déclaration d'une classe simple en un nouvel en-tête et un fichier cpp. Par exemple, comment séparer le code de la classe suivante?

class A2DD
{
  private:
  int gx;
  int gy;

  public:
  A2DD(int x,int y)
  {
    gx = x;
    gy = y;
  }

  int getSum()
  {
    return gx + gy;
  }
};
drdrdr
la source
12
Juste quelques commentaires: le constructeur doit toujours utiliser une liste d'initialisation au lieu de définir les membres dans le corps. Pour une bonne et simple explication, voir: codeguru.com/forum/showthread.php?t=464084 Il est également habituel , au moins dans la plupart des endroits, d'avoir le champ public en haut. Cela n'affectera rien, mais comme les champs publics sont la documentation de votre classe, il est logique de les avoir en haut.
martiert
2
@martiert Le fait d'avoir des public:membres au sommet pouvait affecter beaucoup , si l'utilisateur les déplaçait selon ce conseil - mais avait des dépendances de commande entre les membres et ne savait pas encore que les membres sont initialisés dans l'ordre de leur déclaration ;-)
underscore_d
1
@underscore_d c'est vrai. Mais là encore, nous compilons tous avec des avertissements comme des erreurs et tous les avertissements auxquels nous pouvons penser, non? Cela vous dirait au moins que vous
foutez en l'air
@martiert Bon point, un peu oublié qui génère des avertissements - si seulement les avertissements étaient lus par la plupart :-) Je les utilise et j'essaye de tous les coder. Quelques-uns sont inévitables - alors je dis "merci pour l'avertissement, mais je sais ce que je fais!" - mais la plupart sont mieux corrigés pour éviter toute confusion plus tard.
underscore_d
Avoir des champs publics au sommet n'est qu'un style, que trop ont adopté malheureusement à mon avis. De plus, vous devez garder à l'esprit certaines choses comme @martiert l'a mentionné.
Vassilis

Réponses:

233

La déclaration de classe va dans le fichier d'en-tête. Il est important que vous ajoutiez les #ifndefgardes d'inclusion, ou si vous êtes sur une plate-forme MS, vous pouvez également les utiliser #pragma once. J'ai également omis le privé, par défaut les membres de la classe C ++ sont privés.

// A2DD.h
#ifndef A2DD_H
#define A2DD_H

class A2DD
{
  int gx;
  int gy;

public:
  A2DD(int x,int y);
  int getSum();

};

#endif

et la mise en œuvre va dans le fichier CPP:

// A2DD.cpp
#include "A2DD.h"

A2DD::A2DD(int x,int y)
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}
Ferenc Deak
la source
53
N'oubliez pas que si vous faites de la programmation de modèles, vous devez tout conserver dans le fichier .h pour que le compilateur instancie le bon code au moment de la compilation.
linello
2
avez-vous le #ifndeftruc dans l'en-tête?
Ferenc Deak
4
Cela signifie donc que tous les fichiers qui incluent votre fichier d'en-tête "verront" les membres privés. Si par exemple vous souhaitez publier une lib et son en-tête, vous devez afficher les membres privés de la classe?
Gauthier
1
Non, il y a le merveilleux idiome d'implémentation privée: en.wikipedia.org/wiki/Opaque_pointer Vous pouvez l'utiliser pour cacher les détails d'implémentation.
Ferenc Deak
3
Petit petit bout avec le libellé: "La déclaration de classe va dans le fichier d'en-tête". C'est bien une déclaration, mais c'est aussi une définition, mais comme cette dernière inclut la première, je dirais plutôt que la définition de classe va dans le fichier d'en-tête. Dans l'unité de traduction, vous avez la définition des fonctions membres, pas la définition de la classe. Je suis d'accord, cela vaut peut-être la peine d'être modifié?
lubgr
17

En général, votre .h contient la définition de classe, qui est toutes vos données et toutes vos déclarations de méthode. Comme ça dans votre cas:

A2DD.h:

class A2DD
{
  private:
  int gx;
  int gy;

  public:
  A2DD(int x,int y);    
  int getSum();
};

Et puis votre .cpp contient les implémentations des méthodes comme ceci:

A2DD.cpp:

A2DD::A2DD(int x,int y)
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}
pseudo
la source
7

Il est important de signaler aux lecteurs qui tombent sur cette question lors de la recherche sur le sujet d'une manière plus large que la procédure de réponse acceptée n'est pas nécessaire dans le cas où vous souhaitez simplement diviser votre projet en fichiers. Il n'est nécessaire que lorsque vous avez besoin de plusieurs implémentations de classes uniques. Si votre implémentation par classe est une, un seul fichier d'en-tête pour chacune suffit.

Par conséquent, à partir de l'exemple de la réponse acceptée, seule cette partie est nécessaire:

#ifndef MYHEADER_H
#define MYHEADER_H

//Class goes here, full declaration AND implementation

#endif

Les définitions de préprocesseur #ifndef etc. lui permettent d'être utilisé plusieurs fois.

PS. Le sujet devient plus clair une fois que vous vous rendez compte que C / C ++ est 'stupide' et que #include est simplement une façon de dire "vider ce texte à cet endroit".

j riv
la source
pouvez-vous faire cela en mettant les fichiers "fractionnés" .cpp, ou est-ce .hvraiment "bon" pour cette méthode d'organisation du code?
Benny Jobigan
1
Je pensais que certains projets séparaient les fichiers d'en-tête et d'implémentation (uniques) afin de pouvoir distribuer facilement les fichiers d'en-tête sans révéler le code source des implémentations.
Carl G
Je suis tellement heureux que vous ayez souligné cela parce que j'ai appris à l'origine sur C ++ puis je suis passé à C # il y a de nombreuses années et j'ai récemment fait beaucoup de C ++ et j'ai oublié à quel point le fractionnement des fichiers est fastidieux et ennuyeux et j'ai juste commencé à tout mettre les en-têtes. J'étais à la recherche de quelqu'un qui donnait de bonnes raisons de ne pas faire ça quand j'ai trouvé ça. @CarlG a un bon point, mais à part ce scénario, je pense que tout faire en ligne est la voie à suivre.
Peter Moore
6

Fondamentalement, une syntaxe modifiée de déclaration / définitions de fonction:

a2dd.h

class A2DD
{
private:
  int gx;
  int gy;

public:
  A2DD(int x,int y);

  int getSum();
};

a2dd.cpp

A2DD::A2DD(int x,int y)
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}
Corbin
la source
5

A2DD.h

class A2DD
{
  private:
  int gx;
  int gy;

  public:
  A2DD(int x,int y);

  int getSum();
};

A2DD.cpp

  A2DD::A2DD(int x,int y)
  {
    gx = x;
    gy = y;
  }

  int A2DD::getSum()
  {
    return gx + gy;
  }

L'idée est de conserver toutes les signatures de fonction et tous les membres dans le fichier d'en-tête.
Cela permettra aux autres fichiers de projet de voir à quoi ressemble la classe sans avoir à connaître l'implémentation.

Et en plus de cela, vous pouvez ensuite inclure d'autres fichiers d'en-tête dans l'implémentation au lieu de l'en-tête. Ceci est important car les en-têtes qui sont inclus dans votre fichier d'en-tête seront inclus (hérités) dans tout autre fichier qui inclut votre fichier d'en-tête.

Yochai Timmer
la source
4

Vous laissez les déclarations dans le fichier d'en-tête:

class A2DD
{
  private:
  int gx;
  int gy;

  public:
    A2DD(int x,int y); // leave the declarations here
    int getSum();
};

Et mettez les définitions dans le fichier d'implémentation.

A2DD::A2DD(int x,int y) // prefix the definitions with the class name
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}

Vous pouvez mélanger les deux (laisser la getSum()définition dans l'en-tête par exemple). Ceci est utile car cela donne au compilateur une meilleure chance d'inlining par exemple. Mais cela signifie également que la modification de l'implémentation (si elle est laissée dans l'en-tête) pourrait déclencher une reconstruction de tous les autres fichiers qui incluent l'en-tête.

Notez que pour les modèles, vous devez tout conserver dans les en-têtes.

Tapis
la source
1
Mettre des membres privés et des fonctions dans le fichier d'en-tête n'est pas considéré comme une fuite de détails d'implémentation?
Jason
1
@Jason, en quelque sorte. Ce sont des détails de mise en œuvre nécessaires . Par exemple, je dois savoir combien d'espace une classe consommera sur la pile. Les implémentations de fonction ne sont pas nécessaires pour les autres unités de compilation.
Paul Draper le
1

Habituellement, vous ne mettez que des déclarations et des fonctions en ligne très courtes dans le fichier d'en-tête:

Par exemple:

class A {
 public:
  A(); // only declaration in the .h unless only a short initialization list is used.

  inline int GetA() const {
    return a_;
  }

  void DoSomethingCoplex(); // only declaration
  private:
   int a_;
 };
Ivaylo Strandjev
la source
0

Je ne ferai pas trop référence à votre exemple car il est assez simple pour une réponse générale (par exemple, il ne contient pas de fonctions modèles, ce qui vous oblige à les implémenter sur l'en-tête), ce que je suis en règle générale est le bouton idiome

Cela présente de nombreux avantages car vous obtenez des temps de compilation plus rapides et le sucre syntaxique:

class->member au lieu de class.member

Le seul inconvénient est le pointeur supplémentaire que vous payez.

Spyros Mourelatos
la source