Уместно ли устанавливать значение "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"

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