Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Emplacement des #include

10 réponses
Avatar
Jean-Noël Mégoz
Bonjour,

Je potasse actuellement un cours de C++ et je viens de réaliser un truc qui
me chiffonne dans un exemple :

-- Book.h -------------------------------------
#ifndef BOOK_H
#define BOOK_H

class Reader;
class Reservation;

class Book
{
friend class Reservation;
private:
list<Reservation*> reserv;
[...]
public:
[...]
};
#endif

-- Book.cpp -----------------------------------
#include <list>
using namespace std;

#include "Reader.h"
#include "Book.h"
#include "Reservation.h"

[...]
-----------------------------------------------

A priori, j'aurais mis les #include dans le .h plutôt que dans le .cpp
(chose que j'ai l'habitude de faire en C).
J'aurais alors peut-être eu besoin de répéter l'include de <list> dans
chacun des .h, mais le but n'est-il pas justement de mettre dans ce fichier
tout ce qui est nécessaire au .cpp, autre que le code ?
J'entends par là qu'un éventuel repreneur du module doit, en ne consultant
que le .h, savoir tout de suite ce qui est appelé et appelable...

La solution proposée ici est-elle équivalente/meilleure/la seule
envisageable ?
Pourquoi ?

J.No.

10 réponses

Avatar
Matthieu Moy
"Jean-Noël Mégoz" writes:

A priori, j'aurais mis les #include dans le .h plutôt que dans le .cpp
(chose que j'ai l'habitude de faire en C).


Ce n'est pas une super habitude ...

J'aurais alors peut-être eu besoin de répéter l'include de <list> dans
chacun des .h, mais le but n'est-il pas justement de mettre dans ce fichier
tout ce qui est nécessaire au .cpp, autre que le code ?


Les include du .h sont ceux qui sont nécessaires au .h. Principalement
les définitions de types, et les fonctions utilisées dans les
fonctions inline du .h.

Une bonne habitude est de faire des .h appelables « tout seul », c'est
à dire que toutes les dépendances du .h doivent être dans le .h. Une
mauvaise habitude est de faire un fichier toto.h qui a besoin de
titi.h, ce qui oblige l'appelant à écrire toujours un « #include
"titi.h" » avant chaque « #include "toto.h" ».

Mais si tu utilise, en interne, le type list, alors le #include ne
doit venir que dans le .cpp. Les utilisateurs de ton module n'ont pas
envie de savoir si tes algorithmes utilisent list, vector, ou encore
autre chose.

Ca alourdirait le .h, polluerait ton espace de nom, et ralentirait
même la compilation.

Bref :

.h = interface
.cpp = implémentation

En particulier, si un jour tu as des dépendances croisées

A.h B.h
^ / ^
| X |
| / |
A.cpp B.cpp

Tu ne pourra pas faire autrement sous peine de dépendances cycliques.

--
Matthieu

Avatar
Jean-Noël Mégoz
"Matthieu Moy" a écrit dans le message
de news:
"Jean-Noël Mégoz" writes:

A priori, j'aurais mis les #include dans le .h plutôt que dans le .cpp
(chose que j'ai l'habitude de faire en C).


Ce n'est pas une super habitude ...
[...]
En particulier, si un jour tu as des dépendances croisées

A.h B.h
^ / ^
| X |
| / |
A.cpp B.cpp

Tu ne pourra pas faire autrement sous peine de dépendances cycliques.

Maintenant que tu le dis, je comprends mon erreur.

Et je comprends pourquoi, ayant déjà rencontré ce problème de dépendances
croisées, je n'avais alors pu le résoudre qu'en fusionnant A.h et B.h en un
seul fichier...

Merci.
J.No.


Avatar
Jean-Marc Bourguet
"Jean-Noël Mégoz" writes:

La solution proposée ici est-elle équivalente/meilleure/la seule
envisageable ?


Il y a deux ecoles
- ceux qui ne mettent aucun #include dans un .hxx
- ceux qui considere qu'un fichier ne contenant que
#include "foo.hxx"
doit pouvoir compiler quel que soit foo.hxx
Je fais partie de la seconde -- et plus je touche des projets ou
des gens de la premiere ont sevi, plus je suis convaincu que ma
position est la bonne.

Mais naturellement, il ne faut pas mettre des #include quand une
declaration suffit.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Fabien LE LEZ
On 07 Apr 2004 17:29:57 +0200, Jean-Marc Bourguet
wrote:

- ceux qui considere qu'un fichier ne contenant que
#include "foo.hxx"
doit pouvoir compiler quel que soit foo.hxx


C'est également mon avis.

Par contre, ce qui me chiffonne sérieusement dans le message de
Jean-Noël, c'est le "using namespace std;" avant certains #include.
AMHA, aucun #include ne doit se trouver après une directive "using".


--
;-)

Avatar
Loïc Joly
Jean-Noël Mégoz wrote:

Bonjour,

Je potasse actuellement un cours de C++ et je viens de réaliser un truc qui
me chiffonne dans un exemple :

-- Book.h -------------------------------------
#ifndef BOOK_H
#define BOOK_H

class Reader;
class Reservation;

class Book
{
friend class Reservation;
private:
list<Reservation*> reserv;
[...]
public:
[...]
};
#endif


Un bon principe AMA est qu'un .h doit pouvoir être inclus sans rien
mettre devant. Un autre principe est d'éviter s'inclure ce qui n'est pas
nécessaire, pour minimiser les dépendances entre fichiers.

Dans cet exemple, je suis d'accord avec les
class Reader;
class Reservation;

Mais par contre, la classe a besoin d'utiliser list (std::list ?), et
j'ajouterai un #include <list>.

Enfin, ce .h suppose l'utilisation d'un using namespace, ce qui est moche.

-- Book.cpp -----------------------------------
#include <list>
using namespace std;

#include "Reader.h"
#include "Book.h"
#include "Reservation.h"

[...]
-----------------------------------------------


J'ai pris l'habitude d'include dans un .cpp le .h correspondant en
premier, ainsi, je suis certain que ce .h peut être inclus sans rien
d'autre devant.

--
Loïc

Avatar
kanze
Jean-Marc Bourguet wrote in message
news:...
"Jean-Noël Mégoz" writes:

La solution proposée ici est-elle équivalente/meilleure/la seule
envisageable ?


Il y a deux ecoles
- ceux qui ne mettent aucun #include dans un .hxx
- ceux qui considere qu'un fichier ne contenant que
#include "foo.hxx"
doit pouvoir compiler quel que soit foo.hxx
Je fais partie de la seconde -- et plus je touche des projets ou des
gens de la premiere ont sevi, plus je suis convaincu que ma position
est la bonne.


Je crois que de nos jours, il s'est fait un consensus pour la seconde.

Ce n'était pas toujours le cas, voir
http://www.lysator.liu.se/c/pikestyle.html, par exemple. (Mais je me
démande si Pike aurait écrit les mêmes choses aujourd'hui. J'avoue avoir
été déçu de son dernier livre -- il semblait trop un voyage dans la
passée, avec des conseils sur où on a été, mais qui ne convenait pas
forcément aujourd'hui, malgré leurs efforts de les actualiser.)

Pour la reste, j'avoue ne jamais avoir eu à travailler avec du code de
la première école. Quand ce n'était pas la seconde école, c'était à peu
près n'importe quoi.

Ceci dit, ce n'est pas toujours évident à enforcer la seconde école.
Pour prendre un exemple un peu bête, si j'écris une classe qui dérive de
std::streambuf, et qui contient un membre de type std::string, je dois
logiquement inclure et <streambuf> et <string>. Or, il y a des
implémentations où <streambuf> inclut <string> déjà, ce qui veut dire
que si j'oublis <string>, l'en-tête va compiler quand même tout seul.
Jusqu'à ce que je porte à une autre implémentation.

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Jean-Marc Bourguet
writes:

Jean-Marc Bourguet wrote in message
news:...
"Jean-Noël Mégoz" writes:

La solution proposée ici est-elle équivalente/meilleure/la seule
envisageable ?


Il y a deux ecoles
- ceux qui ne mettent aucun #include dans un .hxx
- ceux qui considere qu'un fichier ne contenant que
#include "foo.hxx"
doit pouvoir compiler quel que soit foo.hxx
Je fais partie de la seconde -- et plus je touche des projets ou des
gens de la premiere ont sevi, plus je suis convaincu que ma position
est la bonne.


Je crois que de nos jours, il s'est fait un consensus pour la
seconde.


J'ai des collegues membres farouches de la premiere avec suffisemment
d'autorite pour l'imposer dans leur projet. Heureusement ce n'est pas
le mien... :-)

Pour la reste, j'avoue ne jamais avoir eu à travailler avec du code de
la première école. Quand ce n'était pas la seconde école, c'était à peu
près n'importe quoi.


Reellement n'importe quoi j'ai pas vu. Mais du code des gens de la
premiere modifie par des gens qui n'ont pas pris l'initiative de
passer clairement a la seconde, ca oui. Generalement j'ajoute alors
les en-tetes qu'il faut a la premiere occasion.

Ceci dit, ce n'est pas toujours évident à enforcer la seconde école.
Pour prendre un exemple un peu bête, si j'écris une classe qui
dérive de std::streambuf, et qui contient un membre de type
std::string, je dois logiquement inclure et <streambuf> et
<string>. Or, il y a des implémentations où <streambuf> inclut
<string> déjà, ce qui veut dire que si j'oublis <string>, l'en-tête
va compiler quand même tout seul. Jusqu'à ce que je porte à une
autre implémentation.


Tu peux avoir exactement le meme probleme en etant de la premiere
ecole, a part que pour le fixer tu devras modifier 1234 fichiers.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org



Avatar
Jan Rendek
Mais par contre, la classe a besoin d'utiliser list (std::list ?), et
j'ajouterai un #include <list>.

Enfin, ce .h suppose l'utilisation d'un using namespace, ce qui est moche.


Pour info, on est jamais obligé d'utiliser une directive 'using' pour
exploiter des classes définies dans un namespace. Il suffit d'utiliser
leur nom complet (std::list) dans ce cas.
L'utilisation d'un 'using namespace XXX' dans un fichier d'entête n'est
pas moche, elle est *à proscrire* absoluement.

Avatar
Fabien LE LEZ
On 8 Apr 2004 00:29:44 -0700, wrote:

Ceci dit, ce n'est pas toujours évident à enforcer la seconde école.
Pour prendre un exemple un peu bête, si j'écris une classe qui dérive de
std::streambuf, et qui contient un membre de type std::string, je dois
logiquement inclure et <streambuf> et <string>. Or, il y a des
implémentations où <streambuf> inclut <string> déjà, ce qui veut dire
que si j'oublis <string>, l'en-tête va compiler quand même tout seul.
Jusqu'à ce que je porte à une autre implémentation.


Pour le coup, il ne s'agit pas d'un problème d'école, juste d'un bug
dans ton code, invisible sur cettains compilos.


--
;-)

Avatar
kanze
Jean-Marc Bourguet wrote in message
news:...
writes:

Jean-Marc Bourguet wrote in message
news:...
"Jean-Noël Mégoz" writes:

La solution proposée ici est-elle équivalente/meilleure/la seule
envisageable ?


Il y a deux ecoles
- ceux qui ne mettent aucun #include dans un .hxx
- ceux qui considere qu'un fichier ne contenant que
#include "foo.hxx"
doit pouvoir compiler quel que soit foo.hxx
Je fais partie de la seconde -- et plus je touche des projets ou
des gens de la premiere ont sevi, plus je suis convaincu que ma
position est la bonne.


Je crois que de nos jours, il s'est fait un consensus pour la
seconde.


J'ai des collegues membres farouches de la premiere avec suffisemment
d'autorite pour l'imposer dans leur projet. Heureusement ce n'est pas
le mien... :-)


En effet.

Dans les premiers jours de C, on pouvait encore en discuter. Mais avec
un langage qui permet des membres privés et des fonctions inline ? Le
profiler dit qu'il faut passer une fonction f() inline, et cette
fonction utilise interne un en-tête qui n'était pas inclu avant. Et tu
veux me dire qu'il y a des gens aujourd'hui pour dire encore qu'il faut
retapper tous les utilisateurs de ta classe, pour ajouter l'include chez
eux ?

Pour la reste, j'avoue ne jamais avoir eu à travailler avec du code
de la première école. Quand ce n'était pas la seconde école, c'était
à peu près n'importe quoi.


Reellement n'importe quoi j'ai pas vu. Mais du code des gens de la
premiere modifie par des gens qui n'ont pas pris l'initiative de
passer clairement a la seconde, ca oui. Generalement j'ajoute alors
les en-tetes qu'il faut a la premiere occasion.


Disons que ce sont en général des gens qui n'ont pas pensé à la
question. Neuf fois sur dix, s'il voit que la classe X utilise l'en-tête
Y, il vont l'inclure dans l'en-tête de la classe X. Mais ils n'y
reflechit pas, et dans la mésure que ça compile dans leur programme de
test (où ils ont bien inclut <string> avant), il y en a la moitié qui
passe à côté.

Je crois que Fabien a dit le mot juste. Si tu négliges un en-tête qu'il
te faut logiquement, c'est une erreur de programmation, même si le code
compile pour l'instant. Et si tu ne pars pas de ce principe (et tu
utilises quand même les include dans les en-têtes), c'est à peu près
n'importe quoi.

Ceci dit, ce n'est pas toujours évident à enforcer la seconde école.
Pour prendre un exemple un peu bête, si j'écris une classe qui
dérive de std::streambuf, et qui contient un membre de type
std::string, je dois logiquement inclure et <streambuf> et <string>.
Or, il y a des implémentations où <streambuf> inclut <string> déjà,
ce qui veut dire que si j'oublis <string>, l'en-tête va compiler
quand même tout seul. Jusqu'à ce que je porte à une autre
implémentation.


Tu peux avoir exactement le meme probleme en etant de la premiere
ecole, a part que pour le fixer tu devras modifier 1234 fichiers.


Ce qui est, je crois, l'argument de base en faveur de la seconde école.

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34