Где объявлять/определять константы области класса в С++?
Мне интересно узнать о преимуществах/недостатках различных деклараций констант и вариантов определения в С++. В течение самого долгого времени я просто объявлял их в верхней части файла заголовка перед определением класса:
//.h
const int MyConst = 10;
const string MyStrConst = "String";
class MyClass {
...
};
Несмотря на то, что это загрязняет глобальное пространство имен (которое, как я знаю, плохо, но никогда не нашел список стилей, почему это плохо), константы по-прежнему будут привязаны к отдельным единицам перевода, поэтому файлы, которые не включить этот заголовок не будет иметь доступа к этим константам. Но вы можете получить коллизии имен, если другие классы определяют константу с тем же именем, что, возможно, не так уж плохо, поскольку это может быть хорошим показателем области, которая может быть реорганизована.
Недавно я решил, что было бы лучше объявить специфические для класса константы внутри самого определения класса:
//.h
class MyClass {
public:
static const int MyConst = 10;
...
private:
static const string MyStrConst;
...
};
//.cpp
const string MyClass::MyStrConst = "String";
Видимость константы будет скорректирована в зависимости от того, используется ли константа только внутри класса или требуется для других объектов, которые используют класс. Это то, что я думаю, является лучшим вариантом сейчас, главным образом потому, что вы можете сохранить внутренние константы класса, приватные для класса, и любые другие классы, использующие общедоступные константы, будут иметь более подробную ссылку на источник константы (например, MyClass:: MyConst). Он также не будет загрязнять глобальное пространство имен. Хотя у него есть недостаток в требовании неинтегральной инициализации в файле cpp.
Я также подумал о переносе констант в их собственный заголовочный файл и обертывании их в пространстве имен, если какой-либо другой класс нуждается в константах, но не во всем определении класса.
Просто ищите мнения и, возможно, другие варианты, которые я еще не рассматривал.
Ответы
Ответ 1
Ваше утверждение о том, что объявление целой константы как статического члена класса "в ущерб требованию неинтегральной инициализации в файле cpp", не является точно твердым, так сказать. Это требует определения в файле cpp, но это не "вред", это вопрос ваших намерений. Объект уровня const
уровня пространства имен в С++ по умолчанию имеет внутреннюю привязку, что означает, что в вашем исходном варианте декларация
const string MyStrConst = "String";
эквивалентно
static const string MyStrConst = "String";
то есть. он определит независимый объект MyStrConst
в каждой единицы перевода, в который включен этот заголовочный файл. Вы знаете об этом? Это было ваше намерение или нет?
В любом случае, если вам не нужен отдельный объект в каждой единицы перевода, объявление константы MyStrConst
в вашем исходном примере не является хорошей практикой. Обычно вы помещаете в файл заголовка не определяющее объявление
extern const string MyStrConst;
и предоставить определение в файле cpp
const string MyStrConst = "String";
поэтому убедитесь, что вся программа использует один и тот же постоянный объект. Другими словами, когда дело доходит до нецелых констант, обычной практикой является их определение в файле cpp. Таким образом, независимо от того, как вы его объявляете (в классе или вне), вам обычно всегда приходится иметь дело с "ущербом" от необходимости определять его в файле cpp. Конечно, как я сказал выше, с константами пространства имен вы можете уйти от того, что у вас есть в первом варианте, но это будет всего лишь примером "ленивого кодирования".
Во всяком случае, я не думаю, что есть причина слишком усложнять проблему: если константа имеет очевидное "вложение" в класс, она должна быть объявлена как член класса.
P.S. Спецификаторы доступа (public
, protected
, private
) не контролируют видимость имени. Они контролируют только его доступность. Имя остается видимым в любом случае.
Ответ 2
Загрязнение глобального пространства имен плохо, потому что кто-то (например, автор используемой библиотеки) может захотеть использовать имя MyConst
для другой цели. Это может привести к серьезным проблемам (библиотеки, которые нельзя использовать вместе и т.д.).
Ваше второе решение, безусловно, лучше всего, если константы связаны с одним классом. Если это не так просто (подумайте о физических или математических константах, не связанных с классом в вашей программе), решение пространства имен лучше этого. BTW: если вы должны быть совместимы с более старыми компиляторами на С++, помните, что некоторые из них не могут использовать интегральную инициализацию в файле заголовка - вы должны инициализировать файл С++ или использовать старый трюк enum
в этом случае.
Я думаю, что нет лучших вариантов для констант - по крайней мере, я не могу думать об одном на данный момент...
Ответ 3
Загрязнение глобального пространства имен должно быть само собой разумеющимся. Если я включаю заголовочный файл, я не хочу сталкиваться или отлаживать конфликты имен с константами, объявленными в этом заголовке. Эти типы ошибок действительно расстраивают и иногда трудно диагностировать. Например, мне когда-то приходилось ссылаться на проект, который был указан в заголовке:
#define read _read
Если ваши константы являются загрязнением пространства имен, это ядерные отходы пространства имен. Проявление этого было серией очень странных ошибок компилятора, жалующихся на отсутствие функции _read, но только при связывании с этой библиотекой. Мы в конечном итоге переименовали функции чтения в нечто другое, что не сложно, но должно быть ненужным.
Ваше второе решение очень разумно, поскольку оно помещает переменную в область видимости. Нет никакой причины, что это должно быть связано с классом, и если мне нужно разделить константы среди классов, я объявлю константы в своем собственном пространстве имен и заголовочном файле. Это не очень удобно для компиляции, но иногда это необходимо.
Я также видел, как люди помещали константы в свой собственный класс, который можно реализовать как синглтон. Это для меня, кажется, работает без вознаграждения, язык предоставляет вам некоторые возможности для объявления констант.
Ответ 4
Лично я использую ваш второй подход; Я использовал его в течение многих лет, и он хорошо работает для меня.
Из точки видимости я хотел бы сделать статическую статистику уровня личных констант, поскольку никто, кроме файла реализации, не должен знать, что они существуют; это помогает предотвратить перекомпиляцию цепной реакции, если вам нужно изменить их имена или добавить новые, поскольку их область имени совпадает с их областью использования...
Ответ 5
Вы можете объявить их как глобальные переменные в файле С++, если они не указаны в заголовке. Затем они являются частными для этого класса и не будут загрязнять глобальное пространство имен.
Ответ 6
Если только один класс будет использовать эти константы, объявите их как static const
внутри тела класса. Если куча связанных классов будет использовать константы, объявите их либо внутри класса/структуры, которая содержит только константы и методы использования или внутри выделенного пространства имен. Например,
namespace MyAppAudioConstants
{
//declare constants here
}
Если они являются константами, используемыми всем приложением (или значительными фрагментами), объявляйте их внутри пространства имен в заголовке, который (либо неявно, либо явно) включен везде.
namespace MyAppGlobalConstants
{
//declare constants here
}
Ответ 7
не загрязняют глобальное пространство имен, загрязняют локальные.
namespace Space
{
const int Pint;
class Class {};
};
Но практически...
class Class
{
static int Bar() {return 357;}
};