Действительно ли уместно установить значение к “символу константы *” в заголовочном файле

Я видел, что люди используют 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, он будет иметь смысл?
15
задан Bart 11 December 2014 в 12:53
поделиться

3 ответа

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

28
ответ дан 1 December 2019 в 00:44
поделиться

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

Лучшим способом достижения того же является следующий (пример 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"

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

5
ответ дан 1 December 2019 в 00:44
поделиться

В чем смысл?

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

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

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

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

10
ответ дан 1 December 2019 в 00:44
поделиться
Другие вопросы по тегам:

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