Подтвердить что ты не робот

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

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

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

#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 в проекте, который включает заголовок. Я попытался переместить определения структуры и структурных переменных в файлы cpp, но тогда другие файлы cpp не могут видеть их или работать с ними, что очень важно, поскольку мне нужны все файлы в проекте, чтобы они могли работать с этими структурами.

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

Обычный способ заключается в том, чтобы не объявлять какие-либо переменные в файлах заголовков. Вместо этого объявите их в одном файле на С++ и объявите их как 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;

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

#define MAINFILE

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

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

Ответ 2

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

Поместите ключевое слово 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];

Ответ 3

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

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

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

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

Ответ 4

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

extern MYSTRUCT Job_Grunt;

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

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

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

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

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

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

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. В моем конкретном случае это не имеет значения; но в противном случае я не мог заставить их работать вместе.

Ответ 9

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