2008-10-21 14 views
14

Donc, peu importe ce que je semble faire, je ne peux pas éviter d'avoir Dev C++ spew nombreuses erreurs de définition multiples à la suite de moi, y compris le même fichier d'en-tête fichiers de code source dans le même projet. Je préfère fortement éviter d'avoir à vider tout mon code source dans un seul fichier et à n'inclure l'en-tête qu'une seule fois, car cela va rendre mon fichier très long et difficile à gérer.Plusieurs erreurs de définition multiple d'inclure le même en-tête dans plusieurs cpps

Essentiellement, voici ce qui se passe:

#ifndef _myheader_h 
#define _myheader_h 

typedef struct MYSTRUCT{ 
int blah; 
int blah2; } MYSTRUCT; 

MYSTRUCT Job_Grunt; 
MYSTRUCT *Grunt = &Job_Grunt; 
MYSTRUCT Job_Uruk; 
MYSTRUCT *Uruk = &Job_Grunt; 

int Other_data[100]; 

void load_jobs(); 

#endif 

Exemple Cpp fichier (ils à peu près tous ressembler à quelque chose comme ça):

#include "myheader.h" 

void load_jobs(){ 

Grunt->blah = 1; 
Grunt->blah2 = 14; 

Uruk->blah = 2; 
Uruk->blah2 = 15; 

return; } 

Gardez à l'esprit que j'ai environ 5 fichiers cpp qui comprennent cet en-tête, chacun traitant d'un type de structure différent trouvé dans le fichier d'en-tête. Dans cet exemple, il y avait seulement une structure contenant un couple de membres, quand il y a environ 4-6 structures différentes avec beaucoup plus de membres dans le fichier d'en-tête réel. Tous les fichiers que j'ai inclus dans suivent la même formule que vous voyez dans cet exemple ici.

Maintenant, je comprends que la garde d'en-tête empêche seulement chaque fichier cpp individuel d'inclure le fichier d'en-tête plus d'une fois. Ce qui semble se produire est que lorsque le compilateur lit le comprennent au début de chaque cpp, il définit le fichier d'en-tête une fois de plus, ce qui est à l'origine de cracher des lignes et des lignes de:

Multiple Definition of Uruk, first defined here 
Multiple Definition of Job_Uruk, first defined here 
Multiple Definition of Grunt, first defined here 
Multiple Definition of Job_Grunt, first defined here 
Multiple Definition of Other_data, first defined here 

I Je vais voir un ensemble de ceci pour à peu près tous les fichiers cpp dans le projet qui comprend l'en-tête. J'ai essayé de déplacer les définitions des variables struct et struct dans les fichiers cpp, mais les autres fichiers cpp ne peuvent pas les voir ou travailler avec eux, ce qui est très important car j'ai besoin de tous les fichiers du projet pour pouvoir travailler avec ces structures.

Mais la partie la plus déroutante au sujet de ce problème, il faut un peu plus d'explications:

La façon dont je suis la mise en place de ces multiples fichiers dans ce projet est identique au livre que je travaille avec, All In One Programmation de jeu par John S. Harbour. J'ai rencontré exactement les mêmes problèmes lorsque j'ai créé les fichiers par exemple des projets dans le livre qui appelaient un en-tête inclus par plusieurs cpps dans le même projet.

Je pourrais les taper, mot à mot du livre, et je veux dire mot à mot ...
et j'obtiendrais la série d'erreurs MD pour chaque cpp dans le projet. Si j'ai chargé l'exemple de projet à partir du CD fourni avec le livre, il serait compilé et fonctionnerait sans problème, bien que les fichiers eux-mêmes, ainsi que les options du projet, soient identiques à ceux que j'avais créés . Si je créais mon propre fichier de projet, et que j'ajoutais simplement les fichiers source et d'en-tête pour le projet exemple à partir du CD, cela aussi compilerait et s'exécuterait, bien que je ne puisse trouver aucune différence entre ceux-ci et moi. J'ai ensuite essayé de créer mon propre fichier de projet, puis de créer les fichiers source et d'en-tête vides et de les ajouter, puis de les copier en les copiant et en les collant à partir des fichiers du CD. à (les mêmes qui avaient travaillé). Et bien sûr, j'obtiendrais la même chose ... des lignes et des lignes de messages d'erreur MD.

Je suis absolument déconcerté.J'ai répété toutes ces méthodes plusieurs fois, et je suis sûr que je ne fais pas de faute de frappe ou que je ne mésestime pas le code. Il semble juste y avoir quelque chose à propos des fichiers premade eux-mêmes; certains paramètres de configuration ou quelque chose d'autre me manque complètement ... ce qui les amènera à compiler correctement alors que les fichiers que je fais ne le seront pas.

Répondre

4

Vous devez définir vos variables en tant qu'ext dans le fichier d'en-tête, puis les définir dans un fichier cpp. i.e .:

extern MYSTRUCT Job_Grunt; 

dans votre fichier d'en-tête, puis dans un fichier cpp dans votre projet les déclarer normalement.

Le fichier d'en-tête est uniquement pour les définitions, lorsque vous instancier une variable dans le fichier d'en-tête, il va essayer de instancier chaque fois que l'en-tête est inclus dans votre projet. L'utilisation de la directive extern indique au compilateur qu'il s'agit simplement d'une définition et que l'instanciation est effectuée ailleurs.

22

Puisque vous déclarer ces variables dans le fichier d'en-tête, et y compris le fichier d'en-tête dans chaque fichier C++, chaque fichier C++ a sa propre copie.

La manière habituelle de contourner ceci est de pas de de déclarer des variables dans les fichiers d'en-tête. Au lieu de cela, déclarez-les dans un seul fichier C++, et déclarez-les comme extern dans tous les autres fichiers que vous pourriez en avoir besoin.

Une autre façon dont j'ai traité cela auparavant, que certaines personnes pourraient considérer désagréable ... déclarer les dans le fichier d'en-tête, comme ceci:

#ifdef MAINFILE 
    #define EXTERN 
#else 
    #define EXTERN extern 
#endif 

EXTERN MYSTRUCT Job_Grunt; 
EXTERN MYSTRUCT *Grunt = &Job_Grunt; 
EXTERN MYSTRUCT Job_Uruk; 
EXTERN MYSTRUCT *Uruk = &Job_Uruk; 

Puis, en un de vos fichiers C++, ajoutez un ...

#define MAINFILE 

... avant vos #include lignes. Cela va prendre soin de tout, et est (à mon avis personnel) beaucoup plus agréable que d'avoir à redéclarer toutes les variables dans chaque fichier.

Bien sûr, la solution réelle ne doit pas du tout utiliser de variables globales, mais lorsque vous commencez à peine, c'est difficile à réaliser.

0

Pour développer ce que Gerald a dit, l'en-tête définit une instance de la structure (ce qui n'est pas ce que vous voulez). Cela provoque chaque unité de compilation (fichier cpp) qui inclut l'en-tête pour obtenir sa propre version de l'instance de struct, ce qui provoque des problèmes au moment de la liaison.

Comme Gerald a dit, vous devez définir une référence à la struct (en utilisant « extern ») dans l'en-tête, et avoir un fichier cpp dans votre projet instancie l'instance.

12

Lorsque vous définissez une variable, le compilateur met de côté la mémoire pour cette variable. En définissant une variable dans le fichier d'en-tête et en incluant ce fichier dans tous vos fichiers source, vous définissez la même variable dans plusieurs fichiers.

Mettre le mot-clé extern avant une définition de variable indiquera au compilateur que cette variable a déjà été défini quelque part, et que vous ne déclarez (à savoir la dénomination) la variable de sorte que d'autres fichiers peuvent l'utiliser. Donc, dans votre fichier d'en-tête, vous devez faire toutes vos définitions déclarations anticipées en ajoutant le mot-clé extern.

extern MYSTRUCT Job_Grunt; 
extern MYSTRUCT *Grunt; 
extern MYSTRUCT Job_Uruk; 
extern MYSTRUCT *Uruk; 

extern int Other_data[100]; 

Et puis dans un (et une seule) de vos fichiers source, définir les variables normalement:

MYSTRUCT Job_Grunt; 
MYSTRUCT *Grunt = &Job_Grunt; 
MYSTRUCT Job_Uruk; 
MYSTRUCT *Uruk = &Job_Grunt; 

int Other_data[100]; 
-3

GCC 3.4 et jusqu'à des supports #pragma once. Il suffit de mettre #pragma once en haut de votre code au lieu d'utiliser des gardes d'inclusion. Cela peut ou ne peut pas être plus réussi, mais ça vaut le coup. Et non, ce n'est pas (toujours) exactement équivalent à un garde d'inclusion.

6

Alors que la plupart des autres réponses sont correctes quant aux raisons pour lesquelles vous voyez plusieurs définitions, la terminologie est imprécise. Comprendre la déclaration par rapport à la définition est la clé de votre problème.

Une déclaration annonce l'existence d'un élément mais ne provoque pas d'instanciation. Par conséquent, les déclarations externes sont des déclarations - pas des définitions.

Une définition crée une instance de l'élément défini. Par conséquent, si vous avez une définition dans un en-tête, elle est instanciée dans chaque fichier .cpp, ce qui entraîne plusieurs définitions. Les définitions sont également des déclarations, c'est-à-dire qu'aucune déclaration séparée n'est nécessaire si, par exemple, la portée de l'élément est limitée à un fichier .cpp.

Remarque: l'utilisation du mot instanciation s'applique uniquement aux éléments de données.

0

Moi aussi j'avais ce problème il y a quelque temps. Laissez-moi essayer d'expliquer ce qui l'a résolu. J'ai eu un fichier global.h qui avait toutes les déclarations et doit être inclus dans chaque fichier cpp. Au lieu de l'inclure dans chaque .cpp, je l'ai inclus dans .h. Tous mes fichiers ".h" j'ai ajouté les lignes #ifndef et #define et fini aveC#endif. Ce problème de MD résolu. J'espère que cela fonctionne pour vous aussi.

0

C'est ce qui a fonctionné pour moi: lier les sources dans des bibliothèques séparées. (Mon problème n'était pas de créer un programme mais une/plusieurs bibliothèques.) J'ai ensuite lié (avec succès) un programme avec les deux bibliothèques que j'ai créées.

J'avais deux ensembles de fonctions (dont l'une dépendait de l'autre) dans le même fichier source, et déclarées dans le même fichier d'en-tête unique. Ensuite, j'ai essayé de séparer les deux ensembles de fonctions dans deux fichiers d'en-tête + source.

J'ai essayé aveC#pragma une fois et j'ai inclus des gardes aveC#ifndef ... #define ... #endif. J'ai également défini les variables et les fonctions comme extern dans les fichiers d'en-tête. Comme l'a souligné Steve Fallows, le problème n'est pas avec la compilation mais avec le couplage. Dans mon problème particulier, je pourrais obtenir deux ensembles de fonctions, chacun dans son propre fichier source, la compilation, puis la liaison dans deux bibliothèques distinctes. Cela me force à lier mes programmes avec libgf.so et libfather.so. Dans mon cas particulier, cela ne fait aucune différence. mais sinon, je ne pouvais pas les amener à travailler ensemble.

2

J'ai également reçu cette erreur pour une fonction définie dans un fichier .h. Le fichier d'en-tête n'était pas destiné à faire des déclarations d'une classe mais des définitions de certaines fonctions qui sont nécessaires à divers endroits dans un projet. (Je peux confondre les usages "definition" et "declaration", mais j'espère pouvoir donner l'idée principale.) Quand je mets un mot-clé "inline" juste avant la définition de la fonction qui donne l'erreur "multiple definitions", erreur est évitée.