Уместно ли устанавливать значение "const char *" в файле заголовка
Я видел людей, использующих 2 метода для объявления и определения char *
.
Medhod 1: заголовочный файл имеет
extern const char* COUNTRY_NAME_USA = "USA";
Меход 2:
Заголовочный файл имеет следующее объявление:
extern const char* COUNTRY_NAME_USA;
Файл cpp имеет следующее определение:
extern const char* COUNTRY_NAME_USA = "USA";
- Является ли метод 1 некорректным?
- В чем разница между двумя?
- Я понимаю разницу между "
const char * const var
" и "const char * var
". Если в приведенных выше методах, если "const char * const var
" объявлен и определен в заголовке, как в методе 1, это имеет смысл?
Ответы
Ответ 1
Первый метод действительно неверен, так как он определяет объект COUNTRY_NAME_USA
с внешней связью в заголовочном файле. Как только этот заголовочный файл включается в несколько единиц перевода, правило одного определения (ODR) нарушается. Код не будет скомпилирован (точнее, он не сможет связать).
Второй метод является правильным. Ключевое слово extern
является необязательным в определении, то есть в файле cpp вы можете просто выполнить
const char* COUNTRY_NAME_USA = "USA"
при условии, что объявление в заголовочном файле предшествует этому определению в этом модуле перевода.
Кроме того, я предполагаю, что, поскольку имя объекта написано с большой буквы, оно, вероятно, должно быть константой. Если это так, то он должен быть объявлен/определен как const char* const COUNTRY_NAME_USA
(обратите внимание на дополнительный const
).
Наконец, учитывая эту последнюю деталь, вы можете просто определить свою константу как
const char* const COUNTRY_NAME_USA = "USA"; // no 'extern'!
в заголовочном файле. Поскольку теперь он является константой, по умолчанию он имеет внутреннюю связь, что означает отсутствие нарушения ODR, даже если заголовочный файл включен в несколько блоков перевода. В этом случае вы получаете отдельное значение COUNTRY_NAME_USA
в каждой единице перевода (в то время как в методе extern
вы получаете единицу для всей программы). Только вы знаете, что вам нужно в вашем случае.
Ответ 2
Какая точка?
Если вы хотите искать строки (которые могут быть локализованы), это было бы лучше:
namespace CountryNames {
const char* const US = "USA";
};
Так как указатель const, он теперь имеет внутреннюю связь и не будет вызывать множественные определения. Большинство компоновщиков также объединяют избыточные константы, поэтому вы не теряете места в исполняемом файле.
Если вы хотите сравнить строки по равенству указателя, то это не переносимо, потому что указатели будут равны, если компоновщик выполняет оптимизацию с постоянной сгибанием. В этом случае объявление указателя extern в файле заголовка - это путь (и он снова должен быть const, если вы не собираетесь перенацеливать его).
Ответ 3
Если у вас должны быть глобальные переменные, обычной практикой является их объявление в файле .h и определение их в одном (и только одном) файле .cpp.
В файле .h;
extern int x;
В файле .cpp;
int x=3;
Я использовал int (самый фундаментальный базовый тип, возможно?), а не const char *, как в вашем примере, потому что суть вашей проблемы не зависит от типа переменной.
Основная идея заключается в том, что вы можете объявить переменную несколько раз, поэтому каждый .cpp файл, содержащий файл .h, объявляет переменную, и это нормально. Но вы только определяете его один раз. Определение - это оператор, в котором вы назначаете начальное значение переменных (с помощью =). Вам не нужны определения в файлах .h, потому что тогда, если файл .h будет включен несколькими файлами .cpp, вы получите несколько определений. Если у вас несколько определений одной переменной, есть проблема во времени связи, потому что компоновщик хочет назначить адрес переменной и не может разумно сделать это, если есть несколько его копий.
Дополнительная информация, добавленная позже, чтобы попытаться облегчить смешение Sud;
Попробуйте уменьшить свою проблему до минимальных частей, чтобы лучше понять их;
Представьте, что у вас есть программа, состоящая из трех файлов .cpp. Для сборки программы каждый .cpp скомпилирован отдельно для создания трех объектных файлов, тогда три объектных файла связаны друг с другом. Если три файла .cpp выглядят следующим образом (пример A, хорошая организация),
file1.cpp
extern int x;
file2.cpp
extern int x;
file3.cpp
extern int x;
Затем файлы будут компилироваться и связываться вместе без проблем (по крайней мере, насколько это касается переменной x). Нет проблем, потому что каждый файл только объявляет переменную x. В декларации просто указывается, что есть где-то переменная, которую я могу (или не могу) использовать.
Лучшим способом достижения одного и того же является следующее (пример A, лучшая организация);
header.h
extern int x;
file1.cpp
#include "header.h"
file2.cpp
#include "header.h"
file3.cpp
#include "header.h"
Это практически то же самое, для каждой из трех компиляций компилятор видит тот же текст, что и раньше, когда он обрабатывает .cpp файл (или блок перевода, как его называют эксперты), потому что директива #include просто тянет текст из другого файла. Тем не менее это улучшает предыдущий пример просто потому, что мы имеем только объявление в одном файле, а не в нескольких файлах.
Теперь рассмотрим другой рабочий пример (пример B, хорошая организация);
file1.cpp
extern int x;
file2.cpp
extern int x;
file3.cpp
extern int x;
int x=3;
Это тоже сработает. Все три файла .cpp объявляют x, и один фактически определяет его. Мы могли бы продолжить и добавить больше кода в функции в любом из трех файлов, которые манипулируют переменной x, и мы не получим никаких ошибок. Снова мы должны использовать заголовочный файл, чтобы объявление включалось только в один физический файл (пример B, лучшая организация).
header.h
extern int x;
file1.cpp
#include "header.h"
file2.cpp
#include "header.h"
file3.cpp
#include "header.h"
int x=3;
Наконец, рассмотрим пример, который просто не сработает (пример C, не работает);
file1.cpp
int x=3;
file2.cpp
int x=3;
file3.cpp
int x=3;
Каждый файл будет скомпилирован без проблем. Проблема возникает во время связи, потому что теперь мы определили три отдельные переменные int x. У них одно и то же имя и все они глобально видны. Задача компоновщика состоит в том, чтобы вытащить все объекты, необходимые для одной программы, в один исполняемый файл. Объекты, видимые во всем мире, должны иметь уникальное имя, чтобы компоновщик мог поместить одну копию объекта по одному определенному адресу (месту) в исполняемый файл и разрешить всем другим объектам получить к нему доступ по этому адресу. В этом случае компоновщик не может выполнять эту работу с глобальной переменной x, поэтому вместо этого будет заглушать ошибку.
В качестве альтернативы различные определения разных начальных значений не решают проблему. Перед каждым определением ключевое слово static решает проблему, потому что теперь переменные не отображаются глобально, а скорее видны внутри .cpp файла, в котором они определены.
Если вы поместите определение глобальной переменной в заголовочный файл, ничего существенного не изменилось (пример C, организация заголовка в этом случае не полезна);
header.h
int x=3; // Don't put this in a .h file, causes multiple definition link error
file1.cpp
#include "header.h"
file2.cpp
#include "header.h"
file3.cpp
#include "header.h"
Фу, надеюсь, кто-то прочитает это и получит от него какую-то выгоду. Иногда вопрошающий кричит о простом объяснении с точки зрения базовых понятий, а не передовых компьютерных ученого объяснения.