Уместно ли установить значение const char * в заголовочном файле - PullRequest
13 голосов
/ 21 мая 2010

Я видел людей, использующих 2 метода для объявления и определения char *.

Medhod 1: заголовочный файл имеет следующие значения:

extern const char* COUNTRY_NAME_USA = "USA";

Medhod 2:
Заголовочный файл имеет следующую декларацию:

extern const char* COUNTRY_NAME_USA;

Файл cpp имеет следующее определение:

extern const char* COUNTRY_NAME_USA = "USA";
  1. Является ли способ 1 неправильным в некотором роде?
  2. В чем разница между этими двумя?
  3. Я понимаю разницу между "const char * const var" и "const char * var". Если в приведенных выше методах, если в заголовке объявлено и определено "const char * const var", как в методе 1, будет ли это иметь смысл?

Ответы [ 3 ]

25 голосов
/ 21 мая 2010

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

9 голосов
/ 21 мая 2010

Какой смысл?

Если вы хотите искать строки (которые могут быть локализованы), это будет лучше всего:

namespace CountryNames {
    const char* const US = "USA";
};

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

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

5 голосов
/ 21 мая 2010

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

В файле .h;

extern int x;

В файле .cpp;

int x=3;

Я использовал int (возможно, самый базовый базовый тип?), А не const char *, как в вашем примере, потому что суть вашей проблемы не зависит от типа переменной.

Основная идея заключается в том, что вы можете объявлять переменную несколько раз, поэтому каждый файл .cpp, содержащий файл .h, объявляет переменную, и это нормально. Но вы определяете это только один раз. Определение - это оператор, в котором вы назначаете начальное значение переменной (с =). Вам не нужны определения в файлах .h, потому что тогда, если файл .h включен в несколько файлов .cpp, вы получите несколько определений. Если у вас есть несколько определений одной переменной, во время компоновки возникает проблема, поскольку компоновщик хочет назначить адрес переменной и не может разумно сделать это, если существует несколько ее копий.

Дополнительная информация добавлена ​​позже, чтобы попытаться облегчить путаницу Суда;

Постарайтесь свести свою проблему к ее минимальным частям, чтобы лучше ее понять;

Представьте, что у вас есть программа, которая содержит три файла .cpp. Для сборки программы каждый .cpp компилируется отдельно для создания трех объектных файлов, затем три объектных файла связываются вместе. Если три файла .cpp следующие (пример A, хорошая организация);

file1.cpp

extern int x;

file2.cpp

extern int x;

file3.cpp

extern int x;

Затем файлы будут скомпилированы и скомпонованы без проблем (по крайней мере, в отношении переменной x). Нет проблем, потому что каждый файл объявляет только переменную x. Объявление просто указывает, что где-то есть переменная, которую я могу (или не могу) использовать.

Лучший способ добиться того же - следующий (пример А, лучшая организация);

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

* * 1068

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"

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...