2010-02-15 4 views
63

Я хочу, чтобы константа в C++ была видимой в нескольких исходных файлах. я могу изображения следующие способы определения его в файле заголовка:Определение глобальной константы в C++

  1. #define GLOBAL_CONST_VAR 0xFF
  2. int GLOBAL_CONST_VAR = 0xFF;
  3. Некоторые функции returing значение (например, int get_GLOBAL_CONST_VAR())
  4. enum { GLOBAL_CONST_VAR = 0xFF; }
  5. const int GLOBAL_CONST_VAR = 0xFF;
  6. extern const int GLOBAL_CONST_VAR; и в одном исходном файле const int GLOBAL_CONST_VAR = 0xFF;

Вариант (1) - это, безусловно, не вариант, вы хотели бы использовать

Вариант (2) - определение экземпляра переменной в каждом файле объекта с помощью заголовочного файла

Option (3) - ИМО над убийства в большинстве случаев

Вариант (4) - во многих случаях может быть не хорошо, так как перечисление не имеет конкретного типа (C++ 0x добавит возможность определить тип)

Таким образом, в наиболее случаях мне нужно t o выберите между (5) и (6). Мои вопросы:

  1. Что вы предпочитаете (5) или (6)?
  2. Почему (5) нормально, а (2) нет?
+0

5 против 2: «const» подразумевает внутреннюю связь. Когда вы включаете этот заголовок версии-5 в несколько единиц перевода, вы не будете нарушать «одно правило определения». Кроме того, const позволяет компилятору выполнять «постоянную фальцовку», тогда как значение переменной, не являющейся константой, может измениться. Вариант 6 неверен. Вам также нужно «extern» в файле cpp, чтобы заставить внешнюю связь, иначе вы получите ошибки компоновщика. Преимущество варианта 6 состоит в том, чтобы скрыть значение. Но это также делает невозможным постоянное складывание. – sellibitze

ответ

26

(5) говорит именно то, что вы хотите сказать. Плюс это позволяет компилятору оптимизировать его большую часть времени. (6), с другой стороны, не позволит компилятору когда-либо оптимизировать его, потому что компилятор не знает, измените ли вы его в конечном итоге или нет.

+0

OTOH, 5 является технически незаконным как нарушение ODR. Однако большинство компиляторов проигнорируют его. – Joel

+0

Эх, я предпочитаю думать об этом как о том, что я вообще ничего не определил, я просто сказал компилятору дать красивое имя ряду. Для всех целей и целей это то, что (5), что означает отсутствие накладных расходов во время выполнения. – Blindy

+1

Является ли (5) нарушением ODR? Если это так, то (6) является предпочтительным.Почему компилятор _ «не знает, измените ли вы его» _ в случае (6)? 'extern const int ...' и 'const int ...' являются постоянными, не так ли? –

5

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

Компилятор может использовать const-характер значения, чтобы расширить некоторые математические данные, а также другие операции, которые используют значение.

Выбор между 5 и 6 - хм; 5 просто чувствует себя лучше для меня.

В 6) значение излишне отделяется от его декларации.

Обычно у меня есть один или несколько из этих заголовков, которые определяют только константы и т. Д., А затем нет другого «умного» материала - приятные легкие заголовки, которые можно легко включить в любом месте.

+3

(6) не является ненужным отстранением, это намеренный выбор. Если у вас много больших констант, вы теряете много места в исполняемом файле, если не объявляете их, как в (6). Это может произойти в математических библиотеках ... отходы могут составлять менее 100 тыс., Но даже это важно иногда. (Некоторые компиляторы имеют другие способы обойти это, я думаю, что MSVC имеет атрибут «один раз» или что-то подобное.) –

+0

С (5) вы не можете быть уверены, что он останется const (вы всегда можете отбросить константа). Вот почему я все же предпочитаю тип перечисления. – fmuecke

+0

@ Дан Олсон - это очень хороший момент. Мой ответ основывался на том, что здесь задействован тип int; но при работе с большими значениями декларация extern - действительно лучший план. –

3
const int GLOBAL_CONST_VAR = 0xFF; 

потому что это постоянный!

+0

И это не будет рассматриваться как макрос, что упростит его отладку. –

57

Определенно идти с опцией 5 - это тип безопасно и позволяет компилятору оптимизировать (не принимать адрес этой переменной :) Кроме того, если это в заголовке - вставить его в пространство имен, чтобы избежать загрязнения глобального масштаба:

// header.hpp 
namespace constants 
{ 
    const int GLOBAL_CONST_VAR = 0xFF; 
    // ... other related constants 

} // namespace constants 

// source.cpp - use it 
#include <header.hpp> 
int value = constants::GLOBAL_CONST_VAR; 
+1

Я получаю ошибку переопределения при попытке включить 'header.hpp' в несколько исходных файлов. – BogdanSikach

2
#define GLOBAL_CONST_VAR 0xFF // this is C code not C++ 
int GLOBAL_CONST_VAR = 0xFF; // it is not constant and maybe not compilled 
Some function returing the value (e.g. int get_LOBAL_CONST_VAR()) // maybe but exists better desision 
enum { LOBAL_CONST_VAR = 0xFF; } // not needed, endeed, for only one constant (enum elms is a simple int, but with secial enumeration) 
const int GLOBAL_CONST_VAR = 0xFF; // it is the best 
extern const int GLOBAL_CONST_VAR; //some compiller doesn't understand this 
1

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

(4) также является достойным выбором, если ваш приоритет гарантирует, что пространство для хранения никогда не выделяется, но оно работает только для интегральных констант.

3

Чтобы ответить на ваш второй вопрос:

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

20

(5) «лучше», чем (6), поскольку он определяет GLOBAL_CONST_VAR как интегральное постоянное выражение (ICE) во всех единицах перевода. Например, вы сможете использовать его как размер массива и как метку case во всех единицах перевода. В случае (6) GLOBAL_CONST_VAR будет ICE только в том блоке перевода, где он определен и только после точки определения. В других единицах перевода он не будет работать как ICE.

Однако, имейте в виду, что (5) дает GLOBAL_CONST_VAR внутреннюю связь, а это означает, что «адрес идентичности» из GLOBAL_CONST_VAR будет отличаться в каждом ЕП, т.е. &GLOBAL_CONST_VAR даст вам другое значение указателя в каждом ЕП. В большинстве случаев это не имеет значения, но если вам понадобится постоянный объект с последовательным глобальным «идентификатором адреса», вам придется пойти с (6), жертвуя ICE-константой в обработать.

Кроме того, когда ICE-константа не является проблемой (а не интегральным типом), а размер типа увеличивается (а не скалярный тип), то (6) обычно становится лучшим подходом, чем (5).

(2) не в порядке, поскольку по умолчанию GLOBAL_CONST_VAR в (2) имеет внешнюю связь. Если вы поместите его в заголовочный файл, вы, как правило, получите несколько определений GLOBAL_CONST_VAR, что является ошибкой. const объекты в C++ имеют внутреннюю привязку по умолчанию, поэтому (5) работает (и именно поэтому, как я сказал выше, вы получаете отдельный независимый GLOBAL_CONST_VAR в каждой единицы перевода).

Смежные вопросы