Повторные ошибки множественного определения при включении одного и того же заголовка в несколько cpps

Таким образом, независимо от того, что я делаю, я не могу избежать того, что Dev C++ из-за меня выдавал многочисленные ошибки множественного определения, включая один и тот же заголовочный файл в несколько файлов исходного кода в одном и том же проекте. Я бы предпочел избегать необходимости сбрасывать весь мой исходный код в один файл и включать заголовок только один раз, так как это сделает мой файл очень длинным и сложным в управлении.

По сути, это то, что происходит:

#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

Пример Cpp-файла (все они выглядят примерно так):

#include "myheader.h"

void load_jobs(){

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

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

return; }

Имейте в виду, что у меня есть около 5 файлов cpp, которые включают этот один заголовок, каждый из которых имеет дело с различным типом структуры, найденной в файле заголовка. В этом примере была только одна структура, содержащая пару элементов, когда в реальном заголовочном файле есть около 4-6 различных структур с гораздо большим числом членов. Все файлы, в которые я включил его, следуют той же формуле, как вы видите в этом примере здесь.

Теперь я понимаю, что защита заголовка только останавливает каждый отдельный файл cpp от включения файла заголовка более одного раза. Казалось бы, что происходит, когда компилятор читает include в начале каждого cpp, он снова определяет файл заголовка, что заставляет его выплевывать строки и строки:

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

Я увижу это примерно для каждого файла cpp в проекте, который включает в себя заголовок. Я попытался перенести определения переменных struct и struct в файлы cpp, но затем другие файлы cpp не могут их видеть или работать с ними, что очень важно, так как мне нужны все файлы в проекте, чтобы иметь возможность работать с этими структурами.

Но самая запутанная часть этой проблемы требует немного большего объяснения:

То, как я настраиваю эти несколько файлов в этом проекте, идентично книге, с которой я работаю, "Все в одной игре" Джона С. Харбора. Я столкнулся с точно такими же проблемами, когда создавал файлы для примеров проектов в книге, в которых требовался один заголовок, включаемый несколькими cpps в одном проекте.

Я мог бы напечатать их слово в слово из книги, и я имею в виду слово в слово...
и я бы получил серию ошибок MD для каждого cpp в проекте.

Если бы я загрузил пример проекта с компакт-диска, входящего в комплект книги, он без проблем скомпилировался бы и запустился, хотя сами файлы, а также параметры проекта, по всей видимости, были идентичны тем, которые я создал.

Если бы я создал свой собственный файл проекта и просто добавил исходные файлы и файлы заголовков для примера проекта с компакт-диска, это также скомпилировало бы и выполнялось, хотя я не могу найти разницы между ними и моими.

Затем я попытался создать свой собственный файл проекта, затем создать пустые исходные файлы и файлы заголовков и добавить их в него, а затем заполнить их, скопировав и вставив их содержимое из файлов на компакт-диск, которому они должны были соответствовать (то же самое). те, которые работали). И конечно же, я получил бы то же самое... строки и строки сообщений об ошибках MD.

Я абсолютно сбит с толку. Я повторил все эти методы несколько раз, и я уверен, что я не ошибаюсь или неправильно пишу код. Кажется, что-то есть в самих готовых файлах; какой-то параметр конфигурации или что-то еще, что я пропускаю полностью... это заставит их правильно компилироваться, а файлы, которые я создаю сам, не будет.

9 ответов

Поскольку вы объявляете эти переменные в заголовочном файле и включаете заголовочный файл в каждый файл C++, каждый файл C++ имеет свою собственную копию.

Обычный способ избежать этого - не объявлять переменные в заголовочных файлах. Вместо этого объявите их в одном файле C++ и объявите их как extern во всех других файлах, которые могут вам понадобиться.

Еще один способ, которым я раньше занимался, который некоторые могут посчитать неприятным... объявить их в заголовочном файле, например так:

#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;

Затем в одном из ваших файлов C++ добавьте...

#define MAINFILE

... до вашего #include линий. Это обо всем позаботится, и (по моему личному мнению) намного приятнее, чем необходимость заново объявлять все переменные в каждом файле.

Конечно, реальное решение - совсем не использовать глобальные переменные, но когда вы только начинаете, это трудно достичь.

Когда вы определяете переменную, компилятор выделяет память для этой переменной. Определив переменную в заголовочном файле и включив этот файл во все ваши исходные файлы, вы определяете одну и ту же переменную в нескольких файлах.

Положить ключевое слово extern прежде чем определение переменной сообщит компилятору, что эта переменная уже была определена где-то, и что вы только объявляете (то есть присваиваете имя) переменную, чтобы другие файлы могли использовать ее.

Таким образом, в вашем заголовочном файле вы должны сделать все свои определения для предварительных объявлений, добавив extern ключевое слово.

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

extern int Other_data[100];

А затем в одном (и только в одном) из ваших исходных файлов обычно определяйте переменные:

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

int Other_data[100];

Хотя большинство других ответов верны относительно того, почему вы видите несколько определений, терминология неточна. Понимание декларации и определения является ключом к вашей проблеме.

Объявление объявляет о существовании предмета, но не вызывает создания экземпляра. Следовательно, внешние заявления являются декларациями, а не определениями.

Определение создает экземпляр определенного элемента. Следовательно, если у вас есть определение в заголовке, оно создается в каждом файле.cpp, что приводит к нескольким определениям. Определения также являются объявлениями - т.е. отдельное объявление не требуется, если, например, область действия элемента ограничена одним файлом.cpp.

Примечание: использование здесь слова "экземпляр" действительно относится только к элементам данных.

Вам необходимо определить ваши переменные как extern в заголовочном файле, а затем определить их также в файле cpp. то есть:

extern MYSTRUCT Job_Grunt;

в вашем заголовочном файле, а затем в файле cpp в вашем проекте объявите их как обычно.

Файл заголовка предназначен только для определений: когда вы создаете экземпляр переменной в файле заголовка, он будет пытаться создавать его каждый раз, когда заголовок включается в ваш проект. Использование директивы extern сообщает компилятору, что это всего лишь определение и что создание экземпляра выполняется где-то еще.

Я также получил эту ошибку для функции, определенной в файле.h. Заголовочный файл не был предназначен для создания объявлений класса, но для определения некоторых функций, которые необходимы в разных местах проекта. (Я могу запутать использование "определения" и "объявления", но я надеюсь, что смогу дать основную идею.) Когда я помещаю ключевое слово "inline" непосредственно перед определением функции, которое выдает ошибку "множественные определения", ошибки избегают

Чтобы расширить сказанное Джеральдом, заголовок определяет экземпляр структуры (а это не то, что вам нужно). Это приводит к тому, что каждый модуль компиляции (файл cpp), который включает в себя заголовок, получает свою собственную версию экземпляра структуры, что вызывает проблемы во время компоновки.

Как сказал Джеральд, вам нужно определить ссылку на структуру (используя 'extern') в заголовке и иметь один файл cpp в вашем проекте, который создает экземпляр экземпляра.

У меня тоже была эта проблема некоторое время назад. Позвольте мне попытаться объяснить, что решило это. У меня был файл global.h, который имел все декларации и должен был быть включен в каждый файл cpp. Вместо того, чтобы включать его в каждый.cpp, я включил его в.h. Во все мои файлы ".h" я добавил строки #ifndef и #define и закончил #endif. Это решило проблему MD. Надеюсь, это работает и для вас.

Вот что сработало для меня: связать источники в отдельные библиотеки. (Моя проблема была не в создании программы, а в одной / нескольких библиотеках.) Затем я (успешно) связал одну программу с двумя созданными мной библиотеками.

У меня было два набора функций (одна зависит от другой) в одном и том же исходном файле, и я был объявлен в одном и том же заголовочном файле. Затем я попытался разделить два набора функций на два заголовка + исходные файлы.

Я попытался с обеими #pragma один раз и включил охрану с #ifndef... #define... #endif. Я также определил переменные и функции как внешние в заголовочных файлах.

Как указал Стив Фэллоуз, проблема не в компиляции, а в связи. В моей конкретной проблеме мне не помешало иметь два набора функций, каждый в своем собственном исходном файле, компилировать и затем связывать в две отдельные библиотеки.

g++ -o grandfather.o -c grandfather.cpp
g++ -o father.o -c father.cpp
g++ -fPIC -shared -o libgf.so grandfather.o
g++ -fPIC -shared -o libfather.so father.o

Это заставляет меня связывать свои программы как с libgf.so, так и с libfather.so. В моем конкретном случае это не имеет значения; но в противном случае я не смог бы заставить их работать вместе.

GCC 3.4 и выше поддерживает #pragma once, Просто положи #pragma once в верхней части вашего кода вместо использования включить охранники. Это может или не может быть более успешным, но это стоит попробовать. И нет, это (не всегда) точно эквивалентно включенной защите.

Другие вопросы по тегам