Оформление константных значений в классах С++
Для себя вместо #define'ов при написании классов принял стиль:
{
...
public:
static const SOME_CONST1 = ...;
static const SOME_CONST2 = ...;
...
};
Есть еще вариант:
{
...
public:
const SOME_CONST1;
const SOME_CONST2;
...
SomeClass(): SOME_CONST1 = ... , SOME_CONST2 = ... {}
};
При таком оформлении они высвечиваются во всплывающей подсказке при кодинге, что довольно удобно. Особенно, если их имена начинать с пары одинаковых символов, чтобы в подсказке все константы были рядом.
Интересно ваше мнение по этому вопросу.
Дефайны - великое Зло.
Забавно. Подобный подход уже давно принят во многих языках. Странно, что вы узнали об этом лишь недавно.
Тип забыл.
Про статические констаны в классе интересно. Я использовал для констант отдельное пространство имен. Надо подумать, что лучше.
Тема инициализации констант в конструкторе не раскрыта. Тут надо сделать возможность задания разных констант для экземпляров класса, иначе лучше вариант со статическими константами.
Нет, ну почему же. Очень часто довольно полезная штука и в плане производительности и в плане удобства. Хотя конечно при отладке могут возникнуть проблемы. Но если работать с ними правильно и не писать подобного
:), то проблем обычно не возникает.
Хотя это дело вкуса каждого.
[QUOTE=nikitozz]...и в плане удобства[/QUOTE]const int someconstant = 5 всегда удобнее, чем #define someconstant 5.
Макросы же, в общем случае, - средство полезное и очень мощное, но стоит трижды подумать, прежде чем вводить в код новый макрос. В любом случае, использование макроподстановок вместо констант лично я считаю недопустимым.
З. Ы. Кстати, в именованном скоупе имена в стиле SOME_CONST1 как-то уже и неактуальны, на мой взгляд...
Во многих случаях макросы следовало бы заменять на inline функции, а от констант, определенных с #define - избавляться.
Почти все пишут, что глобальные переменные и #define это зло. Я согласен, но автор вроде, совсем не про это спрашивал :)
Вот например про #define:
Для себя вместо #define'ов при написании классов принял стиль:
В чём проблемма глобальных констант?
const HOHOHO = 0; написать один раз в верхушке хидера и никаких недоразумений.
Кстати нащёт констант внутри классов. Константы принято писать в верхнем регистре, но таким же образом пишутся и типы. У меня была ситуация, когда такая вот константа(тольков классе как я потом проверил) начинала выдавать такие ошибки в компиляторе и линкере, что найти ошибку было очень тяжело. А всё дело было в том что название константы совпало с каким-то типом. А компилятор начал ругатся вообще на какую то там строчку связанную с проблеммой очень отдалённо. Страшная сказочка))
А какой толк от констант в namespace?
Ответ прост - конкретизация.
Зачем, к примеру, константа basic_string::npos будет глобальной?
А ios::eofbit, ios::badbit ?
100% обьектно ориентированный язык поискать еще нужно, как будто программа будет лучше работать.
Ну к примеру C++, чем не ООП?
Конечно будет лучше работать, т.к. будет разрабатываться лучше.
Если где то понадобится эта константа, еще искать этот класс, потом окажется что этот класс в другой области видимости, а на самом деле в классе с таким же названием константы нету..и т д, а ведь надо было всего то один раз получить эту константу.
Ага, вместо того, чтоб убирать вещи на место, прощу жить на помойке, где все под рукой... только вот иногда можно этой рукой вляпаться в г...но, т.к. на помойке под рукой ВСЁ.
В чём проблемма глобальных констант?
В том же в чем и глобальных переменных,- в их глобальности.
const HOHOHO = 0; написать один раз в верхушке хидера и никаких недоразумений.
А потом думать, чем этот HOHOHO лучше, чем HOHOHO объявленный в соседнем хидере. И к чему он собственно этот HOHOHO относится?
Кстати нащёт констант внутри классов. Константы принято писать в верхнем регистре, но таким же образом пишутся и типы.
Это у кого "принято"?
Давай, не будем обобщать.
У меня была ситуация, когда такая вот константа(тольков классе как я потом проверил) начинала выдавать такие ошибки в компиляторе и линкере, что найти ошибку было очень тяжело. А всё дело было в том что название константы совпало с каким-то типом. А компилятор начал ругатся вообще на какую то там строчку связанную с проблеммой очень отдалённо. Страшная сказочка))
Страшная и неправдоподобная...
Лучше бы её было начать так: "слышал я от одного уважаемого человека...", или даже так: "друг друга брата моего товарища ещё по дет.саду..."
Смысл констант бывает разный:
- применимые к объекту определенного типа;
- глобального назначения;
- ...
При чем тут объектно ориентированность языка?
Зачем напичкивать класс бессмысленными произвольными константами которые могут понадобится всем и которые к классу не относятся?
Класс должен содержать только относящиеся к нему константы, например класс File может содержать константы обозначающие типы открытия файла (на чтение, запись и т.п.). Может пример не очень удачный, т.к. тут вообще лучше перечисление использовать ибн enum. Но это как пример, нет времени выдумывать что-то более в тему.
А зачем она такая нужна, если только 1 раз?
Страшная и неправдоподобная...
Лучше бы её было начать так: "слышал я от одного уважаемого человека...", или даже так: "друг друга брата моего товарища ещё по дет.саду..."
Не люблю спорить потому и не буду ).
А вот насчёт сказочки, то это реальная история которая приключилась со мной. И искал ошибку я очень долго, я был просто в шоке. Не буду описывать как так получилось, но получилось забавно - и это было не обычное совпадение имен.
Насчёт помойки, то это тоже спорный вопрос, потому что помойка это когда что то лежит в одном мусорном контейнере, а что то в другом мусорном контейнере (наблю даешь аналогию??), но это я только про константы.
Абсолютно согласен, но для меня удобство констант еще и в том, что я не должен лезть куда небудь и искать название, возможно это лично мой подход, но для констант я себе оставил вариант быстро найти название, именно потому и пишу сверху хидера.
К примеру для чего делали такие файлы как winbase.h и т. д. Там где списки констант. Да потому что если бы каждый делал в своём классе(так оно и было наверное), и не писал бы в отдельный файл(для напоминаний и часто использования), то была бы действительно огромная помойка.
Я и не говорил, что константы надо писать где-то в другом месте.
Обычно делаю так:
1. Те, что в классах - идут сверху описания класса.
2. Глобальные - сверху хедера или исходника.
Т.е. там где их проще будет найти.
Не люблю спорить потому и не буду ).
Ага, лучше и не надо, т.к. код ты привести не можешь, сообщение об ошибке тоже.
Короче, "давным давно, в старопамятные времена..."
Насчёт помойки, то это тоже спорный вопрос, потому что помойка это когда что то лежит в одном мусорном контейнере, а что то в другом мусорном контейнере (наблю даешь аналогию??), но это я только про константы.
Неа, не наблюдаю.
Для меня помойка - противоположное упорядоченности.
Создается впечатление, что больше одного заголовочного файла в единицу компиляции не включалось... Одноименных констант не встречалось. Констант привязанных к содержимому класса (например, как npos) не находилось...
Насчёт той сказочки с ошибкой, могу на досуге рассказать, еще помню.
Насчёт упорядоченности констант в классах. Смотри есть куча классов. Ты делаешь еще какие либо. В них нужны схожие имена кнстант(для того чтобы в проэкте всё было красиво и каждый программист чисто логически понял назначение новой константы по аналогии со старой). И тут получается что создаётся класс и в нём придумывают соверженно другие константы, бред. Ну и где тут помойка?
Давай не будем рассуждать о том чего не знаем.
Пример, пожалуйста. Не думаю что ты прав. Что значит "придумывают совершенно другие константы"?
Э... не понял к чему это?
Кстати, интересная задачка: придумать минимальную программу с максимальным временем компиляции. :)
Насчёт той сказочки с ошибкой, могу на досуге рассказать, еще помню.
Лучше пример кода покажи и загадочное сообщение об ошибке.
Насчёт упорядоченности констант в классах. Смотри есть куча классов. Ты делаешь еще какие либо. В них нужны схожие имена кнстант(для того чтобы в проэкте всё было красиво и каждый программист чисто логически понял назначение новой константы по аналогии со старой). И тут получается что создаётся класс и в нём придумывают соверженно другие константы, бред. Ну и где тут помойка?
Ну я же тебе уже привел примеры таких констант, где значение может быть различное для разных классов: npos, badbit и т.п.
Могу придумать: max_size, classID, typeID, max_value, и т.п.
Я буду)) когда то згенерированный кусок кода размером под 5метров я пробывал скомпильнуть. Это было зверство. Иде просто умерло.
Пример, пожалуйста. Не думаю что ты прав. Что значит "придумывают совершенно другие константы"?
Я имею ввиду, что допустим касается константа диска, название DSC_OUT, касается файловой системмы, FSS_OUT. А тут появляется нечто вроде HOHOHOHO ))
А причем тут размер генерированного файла и вообще компилирование больших кусков кода к тому что мы обсуждали? Возможно я просто не так тебя понял, потому и написал "Давай не будем рассуждать о том чего не знаем."
как этот ответ относится к тому что я написал по поводу:
Где в твоем примере классы? Напиши конкретнее, пожалуйста, сложно все время догадываться что ты хочешь сказать.
Где в твоем примере классы? Напиши конкретнее, пожалуйста, сложно все время догадываться что ты хочешь сказать.
К щастью мне не сложно догадатся что ты хочешь сказать. У меня своя конкретика. Поподробнее. Нащёт констант. Определяй как хочешь. Хочешь в классе - давай в классе, хочешь вне - можно вне. Что я тебе хотел сказать ? Хотел сказать что лично мне приятнее когда мои константы рядом, названия по стантарту, и когда я смогу всегда их быстро найти. Пример мне лень писать. Если мой стиль не устраивает - пожалуйста, это еще не говорит о том что у меня программы хуже работают. мол, еретик он нарушил постулаты божественного ООП.
Я же не заставляю определять константы только в классах :)
Просто говорил, что их надо определять по смыслу, там где они, так сказать, нужны.
Достаточно удачный пример со строкой (std::string), у которой в качестве константы есть npos, просто, удобно, понятно - и всем известно, что std::string::npos - означает как сказано в МСДН: "An unsigned integral value initialized to –1 that indicates either "not found" or "all remaining characters" when a search function fails"
И всем понятно что эта условность применима только к классу std::string, и использовать данную константу больше ни счем нельзя, т.к. никто не гарантирует, что разработчики не захотят её значение поменять на -2 например, тогда все что касается std::string работать будет корректно, а др. места где ее использовал - врядли.
Вот тебе смысл констант принадлежащих классу.
То, что ее значение менять врядли кто-то будет, это уже др. момент
"Any idiot can write code that computer can compile, but it takes a good programmer to write code that human beings understand." (c) [COLOR="Gray"](сорри, авторства не помню, но плюспиццот однозначно)[/COLOR]
Там списки констант не из-за этого.
WinAPI - процедурная библиотека на C ориентированная, оттого подход процедурный используется.
Но вот отказываться от #define'ов - это уже проблематичней. #define может и зло, и об этом пишут много авторов. НО. Кто из пишущих на C/C++ уже не пишет #define ........... ? Помимо этого существует еще масса процедурных библиотек, который активно используют #define для объявления констант. Работая с ними, волей не волей приходится работать с #define константами. Можно конечно для удобства создать для этих библиотек объектно-ориентированную оболочку и переопределить #define константы в члены класса, но очень часто это нецелесообразно и на это просто нет времени.
1. С одной стороны лучше делать заголовочные файлы, которые используются более чем в двух исходных файлах маленькими, чтобы изменения в них создавались реже. Благодаря этому реже придется перекомпилировать большие куски программы.
Исходя из этого, если в классе много полей, то лучше не делать статические константы класса, а размещать их в отдельном заголовочном файле, в определенном пространстве имен (возможно, в том же, где находится класс).
С другой стороны, тут может быть внесена неясность для пользователя, который не поймет, что класс и константы относятся к одной компоненте (это понятие я тоже пока плохо прочувствовал).
2. Избавиться от констант #define я не могу, так как использую диалоговые окна в ресурсах, а компилятор ресурсов не понимает иные обозначения – не хочет переваривать enum-ы и другие константы.
Пока успокаиваю себя тем, что создание имен констант для обозначения ресурсных объектов имеет определенные традиции, которые не используются при создании других констант. Кроме того, в исходниках других программистов я не видел решение этой проблемы.
Будь добр, не пудри новичкам мозги и не збивай их с толку.
Math.PI - мне нравится :) Глобальные переменные - не столько зло, сколько вопиющий признак неверного планирования взаимодействия компонентов ООП решения в единице компиляции. Я к тому, что проблемы могут быть куда более масштабными, чем те, что связаны с аномнимным пространством имён и модификациией значения разделяемой переменной. А сам по себе запрет, при желании, легко преодалевается: можно ведь применить public static для полей :)
А ты в курсе как вобще директива #include работает? ))
О чем спор не понял. Константы описанные в классе - относятся только к нему (ну максимум к его потомкам) и выносить их кудато не имеет смысла, это же очевидно. И не надо ничего "искать". Если работаем с этим классом - применяем его константы. Если у вас в разных классах получаются одинаковые константы - значит или структура данных неправильно спроектирована или это не одинаковые константы. )
WinAPI - процедурная библиотека на C ориентированная, оттого подход процедурный используется.
Не совсем С, но суть та. Описано там все действительно в стиле С. Приводить ее в пример - смысла нет.
Вот как раз использование в коде const определений и их public static эквивалентов вместо #define совсем не зависит от задачи.
1) Ну я согу сделать с define константой (обычной константой), то что ты не сомжешь сделать с const... Иногда это полезно и удобно.
2) Насчет вне или внутри класса - это зависит от задачи и смысловой нагрузки константы.
Все зависит от задачи.
"Мойте руки перед едой" - тоже догма.
"Уступайте места старшим", "не чавкайте и не говорите с набитым ртом" - тоже догмы.
Однако, они формируют стиль.
Мне вот неприятно общаться с невоспитанным, неопрятным человеком.
Точно так же неприятно читать неопрятный код со свалкой переменных, констант и дефайнов.
Люблю, когда можно выдернуть класс из кода и вставить в другую программу (или тест) с минимальным шаманством и кишками. А любые глобальные вещи (дефайны по определению глобальные) затрудняют такие операции.
Например?
Грубый пример:
В заголовке:
Где-то в коде
И что тут такого неимоверно сложного?
Отсутствие оператора конкатенации строк? :D
Отсутствие оператора конкатенации строк? :D
Где я писал про сложность? Сделай то же самое, но с использованием const и переносимое в любую программу.
Как способ объявления констант - это далеко не лучший выбор. И при альтернативе объявить эту константу членом класса или нэймспейса(к которому она логически относится) или с помощью #define, выигрывает первый вариант.
Но не всегда такая альтернатива существует. Скажем в ситуациях, когда какое-то значение необходимо нам еще на этапе компиляции (предположим для объявления массива).
#define не позволяет выполнять вычислений на этапе компиляции и позиционировать его как средство метапрограммирования - заблуждение.
Максимальная его возможность - подставить текст. Ключевое слово - текст. Макросы как элемент языка препроцессора не предполагают синтаксический и семантический анализ своего тела (а ведь мы имеем дело с текстом программы на формальном языке), и уж тем более не позволяют выполнять пользовательских compile-time вычислений.
[quote=Nixus]Сделай то же самое, но с использованием const и переносимое в любую программу. [/quote]Нафига мне макрос VERSION в каждой программе?
Не понял. Ну объявите размер массива тоже константой. В чем подвох?
А Nixus мухлюет. В его примере скорее макрос, чем константа.
Поэтому я тоже смухлюю на его предложение сделать то же самое :)
{
const int MASSIV_SIZE = 1000;
int massiv[MASSIV_SIZE];
massiv[MASSIV_SIZE - 1] = 999;
const string VERSION = "1.111";
const string DATA = "2008.09.22";
const string OUT_STRING = "Version: " + VERSION + " Data: " + DATA;
cout << OUT_STRING << endl;
// или аналогично
cout << string("Version: " + VERSION + " Data: " + DATA) << endl;
return 0;
}