постоянные переменные, не работающие в заголовке

если я определяю свой постоянный 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;

Это работает

У кого-либо есть Идея, что я мог бы делать неправильно??

Спасибо

55
задан Georg Fritzsche 24 February 2010 в 19:10
поделиться

9 ответов

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

Правильный способ сделать это зависит от вашего намерения.

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

    В 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; 
     
  2. Или вы можете поместить простые не определяющие объявления в файл заголовка и поместить определения в один (и только один) файл реализации

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

129
ответ дан 26 November 2019 в 17:43
поделиться

extern означает, что «реальное» определение переменной находится в другом месте, и компилятор должен быть уверен, что что-то подключится во время компоновки. Странно иметь определение, встроенное в extern , и именно это мешает вашей программе. Если вы хотите, чтобы они были extern , просто определите их ровно один раз где-нибудь в вашей программе.

9
ответ дан 26 November 2019 в 17:43
поделиться

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

1
ответ дан 26 November 2019 в 17:43
поделиться

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

Вверху каждого файла заголовка должно быть что-то вроде:

#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;
1
ответ дан 26 November 2019 в 17:43
поделиться

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

Вам нужно неопределяющее объявление (без инициализатора) в заголовочном файле, а определяющее объявление поместить в один из файлов реализации.

2
ответ дан 26 November 2019 в 17:43
поделиться

Класс extern хранения для них почти наверняка является причиной проблемы, которую вы наблюдаете. Если вы удалите его, код, вероятно, будет в порядке (по крайней мере, в этом отношении).

Edit: Я только что заметил, что вы пометили это как C и C++. В этом отношении C и C++ действительно совершенно разные (но, судя по сообщениям об ошибках, вы, очевидно, компилируете как C++, а не C). В C++ вы хотите удалить extern, потому что (по умолчанию) const переменные имеют класс хранения static. Это означает, что каждый исходный файл (единица трансляции) получит свою собственную "копию" переменной, и не будет никакого конфликта между определениями в разных файлах. Поскольку вы (вероятно) используете только значения, а не рассматриваете их как переменные, наличие нескольких "копий" ничему не повредит - ни одной из них не будет выделено место для хранения.

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

5
ответ дан 26 November 2019 в 17:43
поделиться

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

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

Jacob

1
ответ дан 26 November 2019 в 17:43
поделиться

объявление глобальных const в заголовке приводит к тому, что каждая единица компиляции, включая этот хадер, будет иметь собственные определения глобальных определений с тем же именем. Это не нравится компоновщику.

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

0
ответ дан 26 November 2019 в 17:43
поделиться

Ниже много неверных ответов. Правильны те, которые говорят вам удалить extern , как также сказал в своем комментарии Сэллибитце.

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

2
ответ дан 26 November 2019 в 17:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: