Quelque petite chose sur les using
http://cpp.developpez.com/faq/cpp/?p...PACE_using_std
Version imprimable
Quelque petite chose sur les using
http://cpp.developpez.com/faq/cpp/?p...PACE_using_std
Pour l'erreur, c'était bien celle que j'avais sous les yeux il me semble.8O
Pour le namespace, ...bon, je peux changer les using par des namespace, ok.
par contre, si ça résoud les pb pour les classes GSegment et GLignage, la 3ème et 4ème dans le même processus d'itération (des vecteurs du précédent)... j'ai plus le droit à l'opertor= du vector de la stl (malgrè le namespace) sans 6 erreurs qui dépassent la limite d'affchage de ma console...:?
Salut,
Autant le dire tout de suite, je n'ai lu ni tout le sujet, ni le code que tu as placé.
Cependant, les règles d'or sont:
- Un fichier d'en-tête (*.h/*.hpp) contenant les déclarations et un fichier d'implémentation par classe/ module
- n'inclure, sauf cas très particulier, que les fichiers d'en-tête
- pas de directive using (que ce soit une directive "générale" comme using namespace ou une directive plus ciblée telle que using std::vector) dans les fichiers d'en-tête du fait de la propagation qu'ils occasionnent
- toute fonction appelée, quel que soit l'endroit d'où elle l'est, doit être implémentée une et une seule fois.
Chaque point est susceptible de nécessiter une explication, c'est donc parti :D
[1]Les fichiers d'en-tête ont - uniquement par convention - les extensions .h (pour "header"), .hpp voire, de manière plus ancienne .hxx.
Le pp ou le xx étant simplement là pour indiquer qu'il s'agit d'en-tête destinées au C++ à l'utilisateur du fichier (mais non au compilateur).
Il faut savoir qu'il ne s'agit que de convention car il n'y a aucune vérification du contenu et que le compilateur accepterais sans broncher une inclusion du fichier "monfichier.lextensiondelamortquitue" (du moins, en dehors de toute restriction concernant la longueur des noms de fichiers ;))
La preuve, les fichiers d'en-tête de la bibliothèque standard n'ont aucune extension, et cela fonctionne pourtant très bien ;)
Par déclaration, j'entends:
- la déclaration des variables et ou des fonctions globales (externes, statiques et autres)
- la définition des types (tous types utilisateur confondus: union, classe, structure, énumération voire définition de type)
- le compilateur lit le code de haut en bas, ne connaissant que ce qu'il a déjà rencontré.
Sauf exception, tu ne devrais pas y trouver les implémentation de fonction.
Les exceptions sont:
-les accesseurs et mutateurs "simples" de tes classes et structures (ceux qui se contentent de renvoyer ou de modifier une valeur, sans faire la moindre chose avant ou après)
Il faut alors veiller à ce que ces accesseurs mutateurs soient inline, déclarés et implémentés comme tel
- les fonctions template et les méthodes de classes template car le code exécutable par le processeur ne pourra être fournis que quand le compilateur connaitra le type d'objet sur lequel il doit appliquer le comportement indiqué par cette fonction.
-Les implémentations de fonctions globales déclarée inline
N'oublie pas qu'une méthode virtuelle ne peut, techniquement parlant, pas être inline: le compilateur ne bronchera pas si tu implémente directement une méthode virtuelle dans la définition de la classe, mais, au final, elle ne sera pas inlinée du fait de sa virtualité ;)
Les fichiers d'implémentations doivent, sauf exception, avoir une extension *.cpp, *.cxx, *.C
Il est à noter que le C majuscule n'est valable que sur les systèmes d'exploitation sensible à la casse (la différence entre majuscule et minuscule).
Ces extensions vont servir au compilateur, qui, bien souvent, est en mesure de traiter aussi bien des fichiers contenant du code C que des fichiers contenant du code C++, s'il doit gérer le code comme l'un ou comme l'autre.
Si le compilateur rencontre l'une de ces trois extensions (en prenant en compte la remarque ci-dessus), il saura que ce qu'il va rencontrer sera du code C++, et le traitera comme tel ;)
L'exception consiste en d'éventuels fichiers contenant l'implémentation de fonctions template ou de méthodes de classe template.
En effet, je viens d'expliquer que les fonctions et méthodes de classes template doivent se trouver dans le fichier d'en-tête contenant la déclaration de la fonction ou de la méthode.
Cependant, on peut décider de placer ces implémentations dans des fichiers séparés, pour simplement garder des fichiers d'en-tête plus simple, et il faudra alors recourir à la directive include pour faire le lien.
A ce moment là, pour éviter tout risque de confusion avec un fichier contenant des implémentation classique, il est recommandé d'éviter l'extension .cpp et de lui préférer n'importe quelle autre extension (telle que imp, impl ou autre de ton propre choix)
Si je parle de classe ou de "module", c'est parce qu'il est tout à fait envisageable de déclarer plusieurs classes ou structures "qui vont bien ensemble" dans un seul fichier d'en-tête.
Le choix t'est donné de regrouper ou non les implémentations des fonctions et méthodes (voire, d'autres fonctions et méthodes déclarée dans d'autres fichiers d'en-tête) dans un seul et même fichier d'implémentation, sous réserve d'inclusion des fichiers d'en-tête contenant les déclaration des fonctions et méthodes implémentées.
[2]Le fichier d'en-tête a deux objectifs principaux:
regrouper les déclarations dans un fichier qui soit accessible à l'utilisateur, de manière à lui permettre de vérifier la "forme" de la "chose" (de manière très générale ;)) déclarée, en évitant d'exposer le code qui implémente les différentes fonctions.
permettre au compilateur, grâce à l'inclusion dans un fichier d'implémentation, de savoir que quelque chose existe, et donc de ne pas se plaindre parce qu'il ne connait pas une fonction ou une méthode qui est appelée.
Pour les fichiers d'implémentation, et en dehors des exceptions soulevées plus haut, le but est... de fournir le comportement qui doit être observé lors de l'appel de la fonction ou de la méthode en question.
Chaque fois que le compilateur se rend compte qu'il dispose du "corps" d'une fonction ( du fait d'indiquer ce qui doit être fait dans cette fonction), il crée le code exécutable par le processeur correspondant, sans se soucier de savoir si le code en question existe déjà ou non dans un autre fichier d'implémentation.
Parmis les cas particuliers, on peut citer les fameux fichiers "moc" utilisés sous QT, qui ont généralement l'extension .cpp... Mais il faudra alors veiller à ce que leur inclusion ne se fasse que dans les fichiers d'implémentation, pour éviter tout risque de voir se propager leur contenu ;)
[3]Il faut comprendre que l'inclusion de fichiers d'en-tête est dite "récursive".
Ainsi, si tu as un fichier qui demande l'inclusion de "fichier1.h", que fichier1.h demande lui-même l'inclusion de "fichier2.h" qui demande lui-même l'inclusion de fichier3.h qui demande lui-même l'inclusion de (et on pourrait continuer longtemps comme cela), tu retrouvera l'ensemble des déclarations effectuées dans fichier1.h, fichier2.h, fichier3.h etc.
Si tu place une directive using dans un de ces fichiers, elle se retrouvera donc dans tous les fichiers qui l'incluent de manière directe ou indirecte.
Seulement, voilà...
Si tu place la directive using std::vector dans un fichier, et que par le jeu d'inclusions ce fichier fini par se retrouver dans un fichier dans lequel tu déclare une classe... vector (ou n'importe quel autre nom déjà utilisé par l'une des bibliothèques que tu utilise et pour laquelle tu auras placé la directive using correspondante), simplement, parce que le nom de la classe correspond à ce que tu attend du type d'objet, le compilateur se plaindra de l'ambigüité des noms.
Si toi, tu sais très bien qu'il vaut mieux éviter de créer une classe nommée vector du fait que tu as utilisé la directive using dans un fichiers d'en-tête, tu n'a aucun contrôle sur celui qui utilisera ton travail (et cela peut être toi, à l'occasion d'un nouveau projet, basé sur ton ancien code ;))
Bien que l'utilisateur soit conscient du fait que tu utilise la classe std::vector, il pourra à tout moment décider de créer sa propre classe vector. Et ta directive using viendra mettre le "bordel" dans l'histoire ;)
[4]Il faut bien comprendre que le processus de compilation se fait, en gros, en trois étapes (du moins, en trois étapes qui nous intéressent)
La première étape est le passage du préprocesseur:
Toutes les directives qui commencent par le # sont gérées par le préprocesseur qui peut:
- supprimer une partie de code si une valeur n'est pas connue de lui (ou ne correspond pas à une valeur connue de lui)
- ajouter récursivement le contenu de fichiers (la fameuse directive #include)
- remplacer certaines chaines clairement identifiées par d'autres comme par exemple
régulièrement utilisé lorsque l'on veut créer une bibliothèque dynamique ;)Code:
1
2
3
4
5 #ifdef ALGOEXPORT #define DLLEXPORT __declspec(dllexport) #else #define DLLEXPORT __declspec(dllimport) #endif
La deuxième étape est la création d'un code "objet", qu'il faut comprendre par "la création d'un code utilisable par le compilateur", pour chaque fichier d'implémentation d'un projet.
Pendant son travail, la seule question que le compilateur se posera sera:
et il trouvera la réponse... dans les lignes déjà parcourues du fichiers (auxquelles se sont éventuellement rajoutées les lignes des fichiers inclus, lors de l'étape précédente)Citation:
Est-ce que je connais tel nom (de fonction ou de type) :question:
Si il connait le nom qu'il vient de rencontrer, il remplace ce nom dans le code binaire (exécutable par le processeur) par un "symbole", qui représente ce nom de manière unique dans tous les fichiers compilés.
S'il ne connait pas le nom en question, il se plaint et arrête de travailler.
Comme il "oublie" d'un fichier d'implémentation à un autre ce qu'il a fait, si le malheur veut qu'une fonction (ou une méthode de classe) est implémentée deux fois, ou au contraire, qu'elle n'est implémentée nulle part, mais qu'elle est dument déclarée, le compilateur ne se rendra pas compte de son erreur.
La troisième étape qui nous intéresse est l'édition de liens: un autre outil, l'éditeur de liens prend le relais.
son rôle va être de regrouper tous les fichiers objets en un seul, et de remplacer chaque symbole qu'il rencontre par l'adresse à laquelle le symbole en question commence dans le fichier "reconstitué".
C'est à ce moment là que les erreur "link error" (ou similaires) apparaissent.
Si un symbole est appelé, mais qu'il n'en trouve pas l'implémentation, il se plaindra d'une "référence indéfinie du symbole".
Si, par contre, il se rend compte qu'il existe deux implémentations différentes du même symbole, il ne sera pas en mesure de choisir lequel utiliser pour effectuer la liaison, et se plaindra d'une "référence multiple du symbole".
Au final, il faut principalement veiller à ce que :
- ton EDI soit au courent de TOUTES les "unités de compilation" (couple fichier d'en-tête/fichier d'implémentation)
- Toute méthode déclarée soit définie une fois, et une seule
- tes fichiers soient organisés de manière à ce que tu aies facile, pendant ta période de codage, à faire le lien entre les différentes déclarations et les implémentations qu'elles impliquent.
Une dernière remarque pour la route:
le développement de DevC++ a été arrêté il y a plusieurs années.
Si tu veux un EDI fort proche, mais maintenu à jour, envisage peut-être de passer à Code::Blocks qui a pris la relève de devC++, et qui a réellement le "vent en poupe" :D
merci pour toutes les astuces et pour le cours que j'ai oublié un peu depuis qu'on me l'avait fait! ;)
dans mon cas, j'enlève donc le using namespace par std::vector à chaque fois que j'y fais appel dans le .h ...mais le laisse dans le .cpp
et pour devc++, oui, je l'utilise juste pour vérifier des codes de base, des petites erreurs...