Оформление константных значений в классах С++
Для себя вместо #define'ов при написании классов принял стиль:
{
...
public:
static const SOME_CONST1 = ...;
static const SOME_CONST2 = ...;
...
};
Есть еще вариант:
{
...
public:
const SOME_CONST1;
const SOME_CONST2;
...
SomeClass(): SOME_CONST1 = ... , SOME_CONST2 = ... {}
};
При таком оформлении они высвечиваются во всплывающей подсказке при кодинге, что довольно удобно. Особенно, если их имена начинать с пары одинаковых символов, чтобы в подсказке все константы были рядом.
Интересно ваше мнение по этому вопросу.
Это был пример. Можно найти и другое применение данного поведения. Мы же рассуждаем об общем, а не о частном. Приведи как можно тоже самое сделать с константами тип const char *.
nikitozz сказал все верно:
[QUOTE=nikitozz]говорить о #define вообще, что это зло - по моему не очень правильно.
Как способ объявления констант - это далеко не лучший выбор. И при альтернативе объявить эту константу членом класса или нэймспейса(к которому она логически относится) или с помощью #define, выигрывает первый вариант.[/QUOTE]
Все зависит от задачи.
#define не позволяет выполнять вычислений на этапе компиляции и позиционировать его как средство метапрограммирования - заблуждение.
[/QUOTE]
Я и не имел в виду вычисления на этапе компиляции. Я имел в виду простую подстановку текста. В своей фразе под "чиловыми значениями" я просто понимал обыкновенные числа. Вероятно я неверно выразился. В таком случае приношу извинения. Но даже простая подстановка текста бывает очень часто очень полезной.
О массивах я имел в виду следующее
.................
static const int n;
.................
};
const int MyClass::n = 5;
.......................
int a[MyClass::n];
т.к. в теме чаще всего упоминался такой способ объявления констант. Этот код уже не скомпилится.
Мухлеж в смысле того, что создаем. Например, я не буду спорить, что полезно использовать #define для стражей включения. Но при этом нельзя сказать, что для стражей включения используется более совершенная константа.
А что мешает ее использовать? Она же стандартная - поддерживается нормальными компиляторами, а с правильной оптимизатором строки типа string будут не менее эффективны, чем си-строки.
Долгое время использую языки, где понятие макроподстановки отсутствует в принципе. Не могу представить себе случая, в котором они потребуются в таком виде, в каком их предлагается использовать в C/C++ - это 100% unsafe, презервативы в вида () в которые заключается тело макроса и его параметры - пример тому. Я уверен, ВСЕ задачи можно решить без использования макросов #define (компиляция с условием не в счет).
.................
static const int n;
.................
};
const int MyClass::n = 5;
.......................
int a[MyClass::n];
т.к. в теме чаще всего упоминался такой способ объявления констант. Этот код уже не скомпилится.
А этот?
//.................
public:
static const int n = 5;
//.................
};
int a[MyClass::n];
Мы сравнивали константы объявленные как
const T A = N;
и
#define A N
При чем тут стражи включения? Ты с чем конкретно споришь?
Тебе назвать приложения в которых используется метод который я описал вышеи и не используется STL?
Да что ты? Ты думаешь что код представленный тобой будет такой же по скорости, как описанный мной?
По поводу эффективности STL.
Реализация строк в STL в большинстве случаев оставляет желать лучшего. Тоже самое можно сказать про контейнеры, например map.
//.................
public:
static const int n = 5;
//.................
};
int a[MyClass::n];
А этот работает. Затупил. Не спорю :)
Но дело не конкретно в массивах. Это был лишь так. Примерчик. Как выяснилось - не удачный :)
Вот видите. Отчасти это тоже дело привычек. Вы используете языки где #define не используется по определению. Мне же, как Cишнику, довольно проблематично отказаться от #define. Да, не спорю, любую задачу, которую решает #define, можно решить и без него. Но стоит ли? Возьмем чисто для примера
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif // !UNICODE
Если мне необходимо написать функцию (в данном случае функциИ), поведение которой также будет изменяться в зависимости от наличия UNICODE, да я могу сделать это и иным способом. Не спорю. И несмотря на то, что найдутся те, кто скажет также что этот способ тоже плох. НО. Зачем мне напрягаться и писать что-то новое и сложное, когда есть уже довольно простой способ. Тем более что подобный стиль поведения функций в зависимоти от UNICODE задан изначально. Потому как разрабатывая что-то, используя определенные инструменты, приходится принимать модель и стиль этих инструментов. Да, при разработке на C#,
вопрос #define'ов вообще не стоит никаким образом. Но используя VCL или MFC, довольно сложно отказаться от #define'ов. Я не говорю конкретно о #define константах, я говорю о #define в принципе.
P.S. Предлагаю прекратить этот спор, так как в любом случае каждый останется при своем мнении :) , а разговор все дальше уходит в сторону :)
А кто спорит что нельзя?
ЗЫ. А примера, я так понимаю, не дождусь?
const T A = N;
и
#define A N
При чем тут стражи включения? Ты с чем конкретно споришь?
Я про то, что говорилось о том, что не надо использовать #define для создания констант. Про то, что не надо или надо использовать макросы можно обсуждать в другой теме.
По поводу эффективности STL.
Реализация строк в STL в большинстве случаев оставляет желать лучшего. Тоже самое можно сказать про контейнеры, например map.
Про эффективность string тоже лучше обсуждать в другой теме:
http://forum.codenet.ru/showthread.php?t=46820&page=2
Я думаю, что по скорости они должны быть равны, так как сборка строк в обоих примерах будет на этапе компиляции. А при выводе еще не известно, где будет выигрыш по скорости :)
Так с чем ты споришь? Где там мухлеж?
http://forum.codenet.ru/showthread.php?t=46820&page=2
Я говорил про скорость. А эффективность - это критерий для конкретной задачи.
Бред. Конкатенация string в твоем примере не произойдет на этапе компиляции.
Я говорю про это:
[QUOTE=Nixus]1) Ну я согу сделать с define константой (обычной константой), то что ты не сомжешь сделать с const... Иногда это полезно и удобно.[/QUOTE]
То есть я понимаю, что "обычная константа" и макрос это не одно и то же. И не важно, что они определяются одним способом.
Бред. Конкатенация string в твоем примере не произойдет на этапе компиляции.
Про скорость: все зависит от реализации STL.
Про кантекацию: в константе OUT_STRING она произойдет на этапе компиляции, но прямо в фунции cout она действительно не соберется. Тут я немного погорячился.
Рассмотрим пример для рассчета скорости контекации и вывода:
#include <string>
#include <windows.h>
#define D_VERSION "1.111"
#define D_DATA "2008.09.22"
using namespace std;
const string VERSION = "1.111";
const string DATA = "2008.09.22";
int main()
{
DWORD st1 = 0, st2 = 0;
DWORD sst1 = 0, sst2 = 0;
DWORD ssst1 = 0, ssst2 = 0;
DWORD dt1 = 0, dt2 = 0;
DWORD ddt1 = 0, ddt2 = 0;
st1 = GetTickCount();
for(int i = 0; i < 10000; i++)
cout << string("Version: " + VERSION + ", Build time: " + DATA) << endl;
st2 = GetTickCount();
sst1 = GetTickCount();
const string OUT_STRING = "Version: " + VERSION + ", Build time: " + DATA;
for(int i = 0; i < 10000; i++)
cout << OUT_STRING << endl;
sst2 = GetTickCount();
ssst1 = GetTickCount();
const string OUT_STRINGN = "Version: " + VERSION + ", Build time: " + DATA + "\n";
for(int i = 0; i < 10000; i++)
printf(OUT_STRINGN.c_str());
ssst2 = GetTickCount();
dt1 = GetTickCount();
for(int i = 0; i < 10000; i++)
cout << "Version: " D_VERSION ", Build time: " D_DATA << endl;
dt2 = GetTickCount();
ddt1 = GetTickCount();
for(int i = 0; i < 10000; i++)
printf("Version: " D_VERSION ", Build time: " D_DATA"\n");
ddt2 = GetTickCount();
cout << "make string time: " << st2 - st1 << endl;
cout << "const string time: " << sst2 - sst1 << endl;
cout << "const string time printf: " << ssst2 - ssst1 << endl;
cout << "cout char* time: " << dt2 - dt1 << endl;
cout << "printf char* time: " <<ddt2 - ddt1 << endl;
return 0;
}
Так вот мой компилятор (MinGW - gcc) выдал такие результаты
make string time: 3938
const string time: 3734
const string time printf: 3547
cout char* time: 3719
printf char* time: 3547
Что такое "обычная константа"? В C++ такого понятия вообще нет. Или ты считаешь что нельзя назвать
#define A B
константой?
На протяжении всей темы сравниваются эти два способа определения констант.
А проверить слабо, чтобы глупости не писать? Например, листинг машинного кода с ассемблером?
Зачем ты сравниваешь время вывода? Ты сравни время создания констант...
#define A B
константой?
На протяжении всей темы сравниваются эти два способа определения констант.
Сам с собой споришь? Это твой термин "обычная константа".
Дальше я не буду спорить на эту тему. Я не вижу, что я не прав и не знаю, как убедить собеседника.
Листинг с ассемблером - это реализация для конкретного компилятора. Я же говорил об абстрактом смысле. Как должно быть по логике.
оно и так сравнивалось, ибо константы везде (и в случае с char*) создавались 1 раз на этапе компиляции.
Могу переставить так (хоть это уже не верно):
{
const string OUT_STRING = "Version: " + VERSION + ", Build time: " + DATA;
cout << OUT_STRING << endl;
}
sst2 = GetTickCount();
ssst1 = GetTickCount();
for(int i = 0; i < 10000; i++)
{
const string OUT_STRINGN = "Version: " + VERSION + ", Build time: " + DATA + "\n";
printf(OUT_STRINGN.c_str());
}
ssst2 = GetTickCount();
ничего особо не изменилось. Все в пределах погрешности работы GetTickCount().
Дальше я не буду спорить на эту тему. Я не вижу, что я не прав и не знаю, как убедить собеседника.
Ты не можешь сформулировать конкретно о чем ты споришь? Конечно, тогда не понятно почему ты не прав, т.к. отсутствует предмет спора.
Я написал, что с помощью define могу сделать то что не могу с помощью const, ты спросил как - я показал. Что тебе еще нужно было, какой-то мухлеж выдумал.
Каком еще абстрактном смысле? По какой логике? Компилятор не может оптимизировать конкатенацию string потому что тот работает с динамической памятью, а не статической. Динамическая память создается на этапе исполнения.
Ты проверил листинг, чтобы так утверждать?
...
ничего особо не изменилось. Все в пределах погрешности работы GetTickCount().
Зачем сравнивать производительность вывода через cout и через printf?
Давай определимся.
"Обычной константой" в C++ является именно const, а не define.
Нет такого понятия, как "define константа".
С помощью define задаются макросы. Если макрос представляет собой литерал, то он может служить как константа.
Где-то в коде
Конечно, такой способ лучше, чем приведенный Kogrom.
Но! Здесь макрос используется не как константа.
Макрос здесь подставляется, как часть литерала, поэтому сложно назвать этот пример показательной заменой константы макросом.
Можно привести и другие примеры положительного использования макросов, но мы здесь говорим именно о макросах в качестве констант.
И в этом контексте правильнее использовать константы, а не макросы. А в некоторых случаях использование макросов в качестве констант может привести к путанице и скрытым багам.
"Обычной константой" в C++ является именно const, а не define.
Нет такого понятия, как "define константа".
С помощью define задаются макросы. Если макрос представляет собой литерал, то он может служить как константа.
Конечно, такой способ лучше, чем приведенный Kogrom.
Но! Здесь макрос используется не как константа.
Макрос здесь подставляется, как часть литерала, поэтому сложно назвать этот пример показательной заменой константы макросом.
Можно привести и другие примеры положительного использования макросов, но мы здесь говорим именно о макросах в качестве констант.
И в этом контексте правильнее использовать константы, а не макросы. А в некоторых случаях использование макросов в качестве констант может привести к путанице и скрытым багам.
Вывод: все хорошо в меру. Как я и говорил с самого начала.
Ок. Вероятно, выражение "1) Ну я согу сделать с define константой (обычной константой), то что ты не сомжешь сделать с const... Иногда это полезно и удобно." я должен был истолковывать как "define используется для создания макросов". Ну, тогда вопросов нет.
На счет строк я немного увлекся. Вероятно, действительно только строка char* создастся при компиляции.
Ты высказал сомнение по поводу эффективности кода при использовании STL. Поэтому я проверил, есть ли разница, если делать вывод сишными функциями.
Почему лучше? Он не лучше, он другой. И у него тоже есть недостатки, о которых сам же Green говорил выше.
Например, если программа большая, много классов, и в каждом класссе у нас так задана версия, то не получится распечатать версию для каждого класса. Например, компилятор может переварить следующее (только предупреждение выдаст):
#define VERSION "1.111"
#define VERSION "1.2"
#define VERSION 3.14
#define VERSION 1024
#define VERSION "1.3"
#define VERSION "1.4"
И если я замечу это безобразие, мне придется лазить по всем исходникам, добавлять какой-нибудь отличительный символ каждому дефайну. И каждый раз, как мне пришлют новую версию классов (библиотек, компанент) эту процедуру придется повторять.
Странный вывод...
G: микроскоп - не лучший молоток
N: Это догма, все зависит от обстоятельств. Например, микроскоп можно применить, как научный прибор
G: можно, но мы ж говорим о молотках
N: вывод: все хорошо в меру, как я и говорил
Например, если программа большая, много классов, и в каждом класссе у нас так задана версия, то не получится распечатать версию для каждого класса. Например, компилятор может переварить следующее (только предупреждение выдаст):
#define VERSION "1.111"
#define VERSION "1.2"
#define VERSION 3.14
#define VERSION 1024
#define VERSION "1.3"
#define VERSION "1.4"
И если я замечу это безобразие, мне придется лазить по всем исходникам, добавлять какой-нибудь отличительный символ каждому дефайну. И каждый раз, как мне пришлют новую версию классов (библиотек, компанент) эту процедуру придется повторять.
В том посте у меня есть слово "Но!", за ним следует расшифровка.
Приведенный Nixus-ом макрос используется не как константа, а как часть составного литерала.
Если мне нужно будет сконструировать строковый литерал (не строку), то я выберу такой способ.
Если же понадобится константа, тем более контекстно-зависимая, то это будет именно константа, а не макрос.
Под обычной я понимал что внутри define будет простой литерал, а не вычисление выражения или незаконченная конструкция языка. Ведь литерал можно определить и с помощью const. Но свойства будут разные.
Я выразил сомнение в производительности string и map,а не cout и т.п., и то в том плане, что их аналоги можно сделать более быстрыми, как часто делают, например, в игровых приложениях. Взять хотябы известный Unreal Engine.