Где объявить / определить константы области видимости в C ++? - PullRequest
59 голосов
/ 11 января 2010

Мне интересно узнать о преимуществах / недостатках различных опций объявления и определения констант в C ++. Долгое время я только что объявлял их в верхней части заголовочного файла перед определением класса:

//.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.

Я также рассмотрел вопрос о перемещении констант в их собственный заголовочный файл и оборачивании их в пространство имен на тот случай, если константе нужен какой-то другой класс, но не все определение класса.

Просто ищу мнения и, возможно, другие варианты, которые я еще не рассматривал.

Ответы [ 7 ]

45 голосов
/ 11 января 2010

Ваше утверждение о том, что объявление нецелой константы в качестве статического члена класса "в ущерб требованию нецелой инициализации в файле cpp" не совсем верно, так сказать. Это требует определения в файле cpp, но это не «ущерб», это вопрос вашего намерения. Объект уровня пространства имен const в C ++ по умолчанию имеет внутреннюю связь, что означает, что в исходном варианте объявление

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) не контролируют видимость имени. Они только контролируют его доступность . Имя остается видимым в любом случае.

10 голосов
/ 11 января 2010

Загрязнение глобального пространства имен - это плохо, потому что кто-то (например, создатель используемой вами библиотеки) может захотеть использовать имя MyConst для другой цели. Это может привести к серьезным проблемам (библиотеки, которые нельзя использовать вместе и т. Д.)

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

Я думаю, что нет лучших вариантов для констант - по крайней мере, пока не могу придумать один из них ...

5 голосов
/ 11 января 2010

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

#define read _read

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

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

Я также видел, как люди помещают константы в свой собственный класс, который может быть реализован как одиночный. Мне кажется, это работает без вознаграждения, язык предоставляет вам некоторые возможности для объявления констант.

2 голосов
/ 11 января 2010

Если только один класс будет использовать эти константы, объявите их как static const внутри тела класса. Если группа связанных классов собирается использовать константы, объявите их либо внутри класса / структуры, которая содержит только константы и служебные методы, либо внутри выделенного пространства имен. Например,

namespace MyAppAudioConstants
{
     //declare constants here
}

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

namespace MyAppGlobalConstants
{
    //declare constants here
}
2 голосов
/ 11 января 2010

Лично я использую ваш второй подход; Я использовал его в течение многих лет, и он хорошо работает для меня.

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

2 голосов
/ 11 января 2010

Вы можете объявить их как глобальные в файле c ++, если они не указаны в заголовке. Тогда они являются частными для этого класса и не будут загрязнять глобальное пространство имен.

0 голосов
/ 11 января 2010

не загрязняет глобальное пространство имен, загрязняет локальное.

namespace Space
  {
  const int Pint;
  class Class {};
  };

Но практически ...

class Class
  {
  static int Bar() {return 357;}
  };
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...