если я определяю свой постоянный varibles в моем заголовке как это...
extern const double PI = 3.1415926535;
extern const double PI_under_180 = 180.0f / PI;
extern const double PI_over_180 = PI/180.0f;
Я получаю следующую ошибку
1>MyDirectX.obj : error LNK2005: "double const PI" (?PI@@3NB) already defined in main.obj
1>MyDirectX.obj : error LNK2005: "double const PI_under_180" (?PI_under_180@@3NB) already defined in main.obj
1>MyDirectX.obj : error LNK2005: "double const PI_over_180" (?PI_over_180@@3NB) already defined in main.obj
1>MyGame.obj : error LNK2005: "double const PI" (?PI@@3NB) already defined in main.obj
1>MyGame.obj : error LNK2005: "double const PI_under_180" (?PI_under_180@@3NB) already defined in main.obj
1>MyGame.obj : error LNK2005: "double const PI_over_180" (?PI_over_180@@3NB) already defined in main.obj
но Если я удаляю те константы из заголовка и помещаю их в документ, который включает заголовок как это...
const double PI = 3.1415926535;
const double PI_under_180 = 180.0f / PI;
const double PI_over_180 = PI/180.0f;
Это работает
У кого-либо есть Идея, что я мог бы делать неправильно??
Спасибо
Проблема в том, что вы определяете объекты с внешней связью в файле заголовка. Ожидается, что после включения этого файла заголовка в несколько единиц перевода вы получите несколько определений одного и того же объекта с внешней связью, что является ошибкой.
Правильный способ сделать это зависит от вашего намерения.
Вы можете поместить свои определения в файл заголовка, но убедитесь, что они имеют внутреннюю связь.
В C для этого потребуется явная static
static const double PI = 3.1415926535;
static const double PI_under_180 = 180.0f / PI;
static const double PI_over_180 = PI / 180.0f;
В C ++ static
не является обязательным (поскольку в C ++ объекты const
имеют внутреннюю связь по умолчанию)
const double PI = 3.1415926535;
const double PI_under_180 = 180.0f / PI;
const double PI_over_180 = PI / 180.0f;
Или вы можете поместить простые не определяющие объявления в файл заголовка и поместить определения в один (и только один) файл реализации
объявления в файле заголовка должны включать явные extern
и no initializer
extern const double PI;
extern const double PI_under_180;
extern const double PI_over_180;
и определения в одном файле реализации должны выглядеть следующим образом
const double PI = 3.1415926535;
const double PI_under_180 = 180.0f / PI;
const double PI_over_180 = PI / 180.0f;
(явное extern
в определениях является необязательным, если приведенные выше объявления предшествуют определениям в той же единице перевода).
Какой метод вы выберете, зависит от вашего намерения.
Первый метод упрощает оптимизацию кода компилятору, поскольку он может видеть фактическое значение константы в каждой единице трансляции. Но в то же время концептуально вы получаете отдельные независимые постоянные объекты в каждой единице перевода. Например, & PI
будет оценивать разные адреса в каждой единице трансляции.
Второй метод создает действительно глобальные константы, то есть уникальные объекты констант, которые используются всей программой.Например, & PI
будет оценивать один и тот же адрес в каждой единице трансляции. Но в этом случае компилятор может видеть только фактические значения в одной и только одной единице трансляции, что может помешать оптимизации.
Начиная с C ++ 17 вы получаете третий вариант, который как бы сочетает в себе «лучшее из обоих миров»: встроенные переменные . Встроенные переменные можно безопасно определять в файлах заголовков, несмотря на наличие внешней связи
inline extern const double PI = 3.1415926535;
inline extern const double PI_under_180 = 180.0f / PI;
inline extern const double PI_over_180 = PI/180.0f;
. В этом случае вы получаете именованный константный объект, значение инициализатора которого видно во всех единицах перевода. И в то же время объект имеет внешнюю связь, то есть имеет глобальный адресный идентификатор ( & PI
одинаков во всех единицах трансляции).
Конечно, что-то подобное может быть необходимо только для некоторых экзотических целей (большинство случаев использования в C ++ вызывает первый вариант), но такая возможность есть.
extern
означает, что «реальное» определение переменной находится в другом месте, и компилятор должен быть уверен, что что-то подключится во время компоновки. Странно иметь определение, встроенное в extern
, и именно это мешает вашей программе. Если вы хотите, чтобы они были extern
, просто определите их ровно один раз где-нибудь в вашей программе.
Если вы хотите определить константы в файлах заголовков, используйте static const
. Если вы используете extern
, компоновщик вправе жаловаться на несколько определений, потому что каждый включающий исходный файл будет предоставлять память для переменной, если вы присваиваете значение.
Похоже, этот заголовочный файл включается несколько раз. Вам нужно добавить охранников.
Вверху каждого файла заголовка должно быть что-то вроде:
#ifndef MY_HEADER_FILE_NAME_H
#define MY_HEADER_FILE_NAME_H
...
// at end of file
#endif
Если вы используете g ++ или MSVC, вы можете просто добавить:
#pragma once
Вверху каждого файла заголовка, но это не 100% портативный.
Кроме того, вы не должны определять константы в файлах заголовков, только объявляйте их:
// In header file
extern const int my_const;
// In one source file
const int my_const = 123;
Проблема в том, что вы инициализируете переменные в заголовочном файле; это создает определяющее объявление, которое повторяется в каждом файле, включающем этот заголовок, отсюда и ошибка множественного определения.
Вам нужно неопределяющее объявление (без инициализатора) в заголовочном файле, а определяющее объявление поместить в один из файлов реализации.
Класс extern
хранения для них почти наверняка является причиной проблемы, которую вы наблюдаете. Если вы удалите его, код, вероятно, будет в порядке (по крайней мере, в этом отношении).
Edit: Я только что заметил, что вы пометили это как C и C++. В этом отношении C и C++ действительно совершенно разные (но, судя по сообщениям об ошибках, вы, очевидно, компилируете как C++, а не C). В C++ вы хотите удалить extern
, потому что (по умолчанию) const
переменные имеют класс хранения static
. Это означает, что каждый исходный файл (единица трансляции) получит свою собственную "копию" переменной, и не будет никакого конфликта между определениями в разных файлах. Поскольку вы (вероятно) используете только значения, а не рассматриваете их как переменные, наличие нескольких "копий" ничему не повредит - ни одной из них не будет выделено место для хранения.
В C, extern
довольно сильно отличается, и удаление extern
не даст никакой реальной разницы, потому что они будут extern
по умолчанию. В этом случае вам действительно нужно инициализировать переменные в одном месте и объявить их extern в заголовке. В качестве альтернативы вы можете добавить класс хранения static
, который C++ добавит по умолчанию, когда/если вы удалите extern
из заголовка.
Вам нужно объявить константы в заголовке, а затем определить их в одном из ваших файлов кода. Если вы нигде не объявите их, то произойдет ошибка компоновщика, когда он попытается связать объявление с фактическим определением. Вы также можете обойтись использованием утверждений #ifdef, чтобы иметь одно определение в заголовке.
Убедитесь, что они объявлены в заголовке, который включается всеми, кому они нужны, и убедитесь, что они определены только один раз.
Jacob
объявление глобальных const в заголовке приводит к тому, что каждая единица компиляции, включая этот хадер, будет иметь собственные определения глобальных определений с тем же именем. Это не нравится компоновщику.
Если вам действительно нужны эти определения в заголовке, то, вероятно, вам следует объявить их как статические.
Ниже много неверных ответов. Правильны те, которые говорят вам удалить extern
, как также сказал в своем комментарии Сэллибитце.
Поскольку они объявлены как константы, нет проблем с определением в заголовке. C ++ встроит константу для встроенного типа, если вы не попытаетесь получить ее адрес (указатель на константу), и в этом случае он создаст его с помощью статической
связи, тогда вы также можете получить несколько экземпляров в отдельных модулей, но если вы не ожидаете, что все указатели на одну и ту же константу будут иметь один и тот же адрес, это не проблема.