Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Оформление константных значений в классах С++

535
19 сентября 2008 года
Нездешний
537 / / 17.01.2008
Как-то понял, что значительную часть обращений к хелпам (у меня лично), составляет поиск констант, определенных для функции или класса. Причем часто бывают случаи, когда вроде бы помнишь, что была какая-то константа, но не помнишь точного написания. Лезть только за тем, чтобы посмотреть "ios::trunc" там, или "ios::truncate", довольно напрягает.
Для себя вместо #define'ов при написании классов принял стиль:
 
Код:
class SomeClass
{
...
public:
       static const SOME_CONST1 = ...;
       static const SOME_CONST2 = ...;
...
};

Есть еще вариант:
 
Код:
class SomeClass
{
...
public:
      const SOME_CONST1;
      const SOME_CONST2;
...
      SomeClass():  SOME_CONST1 = ... , SOME_CONST2 = ... {}
};

При таком оформлении они высвечиваются во всплывающей подсказке при кодинге, что довольно удобно. Особенно, если их имена начинать с пары одинаковых символов, чтобы в подсказке все константы были рядом.
Интересно ваше мнение по этому вопросу.
Страницы:
353
22 сентября 2008 года
Nixus
840 / / 04.01.2007
Цитата: hardcase
Нафига мне макрос VERSION в каждой программе?


Это был пример. Можно найти и другое применение данного поведения. Мы же рассуждаем об общем, а не о частном. Приведи как можно тоже самое сделать с константами тип const char *.

nikitozz сказал все верно:
[QUOTE=nikitozz]говорить о #define вообще, что это зло - по моему не очень правильно.
Как способ объявления констант - это далеко не лучший выбор. И при альтернативе объявить эту константу членом класса или нэймспейса(к которому она логически относится) или с помощью #define, выигрывает первый вариант.[/QUOTE]

Все зависит от задачи.

353
22 сентября 2008 года
Nixus
840 / / 04.01.2007
Цитата: Kogrom
А Nixus мухлюет. В его примере скорее макрос, чем константа.


Не поверишь, с помощью define определяются только макросы. Где мухлеж?

Цитата: Kogrom
Поэтому я тоже смухлюю на его предложение сделать то же самое :)


Пример требует стандартной библиотеки C++, а что если я ее не использую?

288
22 сентября 2008 года
nikitozz
1.2K / / 09.03.2007
[QUOTE=hardcase]
#define не позволяет выполнять вычислений на этапе компиляции и позиционировать его как средство метапрограммирования - заблуждение.
[/QUOTE]
Я и не имел в виду вычисления на этапе компиляции. Я имел в виду простую подстановку текста. В своей фразе под "чиловыми значениями" я просто понимал обыкновенные числа. Вероятно я неверно выразился. В таком случае приношу извинения. Но даже простая подстановка текста бывает очень часто очень полезной.

Цитата: Kogrom
Не понял. Ну объявите размер массива тоже константой. В чем подвох?



О массивах я имел в виду следующее

 
Код:
class MyClass {
.................
static const int n;
.................
};
const int MyClass::n = 5;
.......................
int a[MyClass::n];

т.к. в теме чаще всего упоминался такой способ объявления констант. Этот код уже не скомпилится.
87
22 сентября 2008 года
Kogrom
2.7K / / 02.02.2008
Цитата: Nixus
Не поверишь, с помощью define определяются только макросы. Где мухлеж?


Мухлеж в смысле того, что создаем. Например, я не буду спорить, что полезно использовать #define для стражей включения. Но при этом нельзя сказать, что для стражей включения используется более совершенная константа.

Цитата: Nixus
Пример требует стандартной библиотеки C++, а что если я ее не использую?


А что мешает ее использовать? Она же стандартная - поддерживается нормальными компиляторами, а с правильной оптимизатором строки типа string будут не менее эффективны, чем си-строки.

5
22 сентября 2008 года
hardcase
4.5K / / 09.08.2005
Цитата: nikitozz
простая подстановка текста бывает очень часто очень полезной

Долгое время использую языки, где понятие макроподстановки отсутствует в принципе. Не могу представить себе случая, в котором они потребуются в таком виде, в каком их предлагается использовать в C/C++ - это 100% unsafe, презервативы в вида () в которые заключается тело макроса и его параметры - пример тому. Я уверен, ВСЕ задачи можно решить без использования макросов #define (компиляция с условием не в счет).

87
22 сентября 2008 года
Kogrom
2.7K / / 02.02.2008
Цитата: nikitozz
О массивах я имел в виду следующее
 
Код:
class MyClass {
.................
static const int n;
.................
};
const int MyClass::n = 5;
.......................
int a[MyClass::n];

т.к. в теме чаще всего упоминался такой способ объявления констант. Этот код уже не скомпилится.


А этот?

 
Код:
class MyClass {
//.................
public:
    static const int n = 5;
//.................
};
int a[MyClass::n];
353
22 сентября 2008 года
Nixus
840 / / 04.01.2007
Цитата: Kogrom
Мухлеж в смысле того, что создаем. Например, я не буду спорить, что полезно использовать #define для стражей включения. Но при этом нельзя сказать, что для стражей включения используется более совершенная константа.


Мы сравнивали константы объявленные как
const T A = N;
и
#define A N
При чем тут стражи включения? Ты с чем конкретно споришь?

Цитата: Kogrom
А что мешает ее использовать? Она же стандартная - поддерживается нормальными компиляторами,


Тебе назвать приложения в которых используется метод который я описал вышеи и не используется STL?

Цитата: Kogrom
а с правильной оптимизатором строки типа string будут не менее эффективны, чем си-строки.


Да что ты? Ты думаешь что код представленный тобой будет такой же по скорости, как описанный мной?
По поводу эффективности STL.
Реализация строк в STL в большинстве случаев оставляет желать лучшего. Тоже самое можно сказать про контейнеры, например map.

288
22 сентября 2008 года
nikitozz
1.2K / / 09.03.2007
Цитата: Kogrom
А этот?
 
Код:
class MyClass {
//.................
public:
    static const int n = 5;
//.................
};
int a[MyClass::n];


А этот работает. Затупил. Не спорю :)
Но дело не конкретно в массивах. Это был лишь так. Примерчик. Как выяснилось - не удачный :)

Цитата: hardcase
Долгое время использую языки, где понятие макроподстановки отсутствует в принципе. Не могу представить себе случая, в котором они потребуются в таком виде, в каком их предлагается использовать в C/C++ - это 100% unsafe, презервативы в вида () в которые заключается тело макроса и его параметры - пример тому. Я уверен, ВСЕ задачи можно решить без использования макросов #define (компиляция с условием не в счет).



Вот видите. Отчасти это тоже дело привычек. Вы используете языки где #define не используется по определению. Мне же, как Cишнику, довольно проблематично отказаться от #define. Да, не спорю, любую задачу, которую решает #define, можно решить и без него. Но стоит ли? Возьмем чисто для примера

 
Код:
#ifdef UNICODE
#define MessageBox  MessageBoxW
#else
#define MessageBox  MessageBoxA
#endif // !UNICODE

Если мне необходимо написать функцию (в данном случае функциИ), поведение которой также будет изменяться в зависимости от наличия UNICODE, да я могу сделать это и иным способом. Не спорю. И несмотря на то, что найдутся те, кто скажет также что этот способ тоже плох. НО. Зачем мне напрягаться и писать что-то новое и сложное, когда есть уже довольно простой способ. Тем более что подобный стиль поведения функций в зависимоти от UNICODE задан изначально. Потому как разрабатывая что-то, используя определенные инструменты, приходится принимать модель и стиль этих инструментов. Да, при разработке на C#,
вопрос #define'ов вообще не стоит никаким образом. Но используя VCL или MFC, довольно сложно отказаться от #define'ов. Я не говорю конкретно о #define константах, я говорю о #define в принципе.

P.S. Предлагаю прекратить этот спор, так как в любом случае каждый останется при своем мнении :) , а разговор все дальше уходит в сторону :)
353
22 сентября 2008 года
Nixus
840 / / 04.01.2007
Цитата: hardcase
Я уверен, ВСЕ задачи можно решить без использования макросов #define (компиляция с условием не в счет).


А кто спорит что нельзя?
ЗЫ. А примера, я так понимаю, не дождусь?

87
22 сентября 2008 года
Kogrom
2.7K / / 02.02.2008
Цитата: Nixus
Мы сравнивали константы объявленные как
const T A = N;
и
#define A N
При чем тут стражи включения? Ты с чем конкретно споришь?


Я про то, что говорилось о том, что не надо использовать #define для создания констант. Про то, что не надо или надо использовать макросы можно обсуждать в другой теме.

Цитата: Nixus
Да что ты? Ты думаешь что код представленный тобой будет такой же по скорости, как описанный мной?
По поводу эффективности STL.
Реализация строк в STL в большинстве случаев оставляет желать лучшего. Тоже самое можно сказать про контейнеры, например map.


Про эффективность string тоже лучше обсуждать в другой теме:
http://forum.codenet.ru/showthread.php?t=46820&page=2

Я думаю, что по скорости они должны быть равны, так как сборка строк в обоих примерах будет на этапе компиляции. А при выводе еще не известно, где будет выигрыш по скорости :)

353
22 сентября 2008 года
Nixus
840 / / 04.01.2007
Цитата: Kogrom
Я про то, что говорилось о том, что не надо использовать #define для создания констант. Про то, что не надо или надо использовать макросы можно обсуждать в другой теме.


Так с чем ты споришь? Где там мухлеж?

Цитата: Kogrom
Про эффективность string тоже лучше обсуждать в другой теме:
http://forum.codenet.ru/showthread.php?t=46820&page=2


Я говорил про скорость. А эффективность - это критерий для конкретной задачи.

Цитата: Kogrom
Я думаю, что по скорости они должны быть равны, так как сборка строк в обоих примерах будет на этапе компиляции.


Бред. Конкатенация string в твоем примере не произойдет на этапе компиляции.

87
22 сентября 2008 года
Kogrom
2.7K / / 02.02.2008
Цитата: Nixus
Так с чем ты споришь? Где там мухлеж?


Я говорю про это:
[QUOTE=Nixus]1) Ну я согу сделать с define константой (обычной константой), то что ты не сомжешь сделать с const... Иногда это полезно и удобно.[/QUOTE]
То есть я понимаю, что "обычная константа" и макрос это не одно и то же. И не важно, что они определяются одним способом.

Цитата: Nixus
Я говорил про скорость. А эффективность - это критерий для конкретной задачи.

Бред. Конкатенация string в твоем примере не произойдет на этапе компиляции.


Про скорость: все зависит от реализации STL.
Про кантекацию: в константе OUT_STRING она произойдет на этапе компиляции, но прямо в фунции cout она действительно не соберется. Тут я немного погорячился.

Рассмотрим пример для рассчета скорости контекации и вывода:

Код:
#include <iostream>
#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
353
22 сентября 2008 года
Nixus
840 / / 04.01.2007
Цитата: Kogrom
То есть я понимаю, что "обычная константа" и макрос это не одно и то же. И не важно, что они определяются одним способом.


Что такое "обычная константа"? В C++ такого понятия вообще нет. Или ты считаешь что нельзя назвать
#define A B
константой?
На протяжении всей темы сравниваются эти два способа определения констант.

Цитата: Kogrom
Про кантекацию: в константе OUT_STRING она произойдет на этапе компиляции


А проверить слабо, чтобы глупости не писать? Например, листинг машинного кода с ассемблером?

Цитата: Kogrom
Рассмотрим пример для рассчета скорости контекации и вывода:


Зачем ты сравниваешь время вывода? Ты сравни время создания констант...

87
22 сентября 2008 года
Kogrom
2.7K / / 02.02.2008
Цитата: Nixus
Что такое "обычная константа"? В C++ такого понятия вообще нет. Или ты считаешь что нельзя назвать
#define A B
константой?
На протяжении всей темы сравниваются эти два способа определения констант.


Сам с собой споришь? Это твой термин "обычная константа".
Дальше я не буду спорить на эту тему. Я не вижу, что я не прав и не знаю, как убедить собеседника.

Цитата: Nixus
А проверить слабо, чтобы глупости не писать? Например, листинг машинного кода с ассемблером?


Листинг с ассемблером - это реализация для конкретного компилятора. Я же говорил об абстрактом смысле. Как должно быть по логике.

Цитата: Nixus
Зачем ты сравниваешь время вывода? Ты сравни время создания констант...


оно и так сравнивалось, ибо константы везде (и в случае с char*) создавались 1 раз на этапе компиляции.
Могу переставить так (хоть это уже не верно):

Код:
for(int i = 0; i < 10000; i++)
    {
        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().
353
22 сентября 2008 года
Nixus
840 / / 04.01.2007
Цитата: Kogrom
Сам с собой споришь? Это твой термин "обычная константа".
Дальше я не буду спорить на эту тему. Я не вижу, что я не прав и не знаю, как убедить собеседника.


Ты не можешь сформулировать конкретно о чем ты споришь? Конечно, тогда не понятно почему ты не прав, т.к. отсутствует предмет спора.
Я написал, что с помощью define могу сделать то что не могу с помощью const, ты спросил как - я показал. Что тебе еще нужно было, какой-то мухлеж выдумал.

Цитата: Kogrom
Листинг с ассемблером - это реализация для конкретного компилятора. Я же говорил об абстрактом смысле. Как должно быть по логике.


Каком еще абстрактном смысле? По какой логике? Компилятор не может оптимизировать конкатенацию string потому что тот работает с динамической памятью, а не статической. Динамическая память создается на этапе исполнения.

Цитата: Kogrom
оно и так сравнивалось, ибо константы везде (и в случае с char*) создавались 1 раз на этапе компиляции.


Ты проверил листинг, чтобы так утверждать?

Цитата: Kogrom
Могу переставить так (хоть это уже не верно):
...
ничего особо не изменилось. Все в пределах погрешности работы GetTickCount().


Зачем сравнивать производительность вывода через cout и через printf?

3
22 сентября 2008 года
Green
4.8K / / 20.01.2000
Цитата: Nixus
1) Ну я согу сделать с define константой (обычной константой), то что ты не сомжешь сделать с const... Иногда это полезно и удобно.


Давай определимся.
"Обычной константой" в C++ является именно const, а не define.
Нет такого понятия, как "define константа".
С помощью define задаются макросы. Если макрос представляет собой литерал, то он может служить как константа.

Цитата: Nixus

Где-то в коде
 
Код:
Renderer->TextOut(0, 0, "Version: " VERSION ", Build time: " BUILD_TIME);


Конечно, такой способ лучше, чем приведенный Kogrom.
Но! Здесь макрос используется не как константа.
Макрос здесь подставляется, как часть литерала, поэтому сложно назвать этот пример показательной заменой константы макросом.

Можно привести и другие примеры положительного использования макросов, но мы здесь говорим именно о макросах в качестве констант.
И в этом контексте правильнее использовать константы, а не макросы. А в некоторых случаях использование макросов в качестве констант может привести к путанице и скрытым багам.

353
22 сентября 2008 года
Nixus
840 / / 04.01.2007
Цитата: Green
Давай определимся.
"Обычной константой" в C++ является именно const, а не define.
Нет такого понятия, как "define константа".
С помощью define задаются макросы. Если макрос представляет собой литерал, то он может служить как константа.


Конечно, такой способ лучше, чем приведенный Kogrom.
Но! Здесь макрос используется не как константа.
Макрос здесь подставляется, как часть литерала, поэтому сложно назвать этот пример показательной заменой константы макросом.

Можно привести и другие примеры положительного использования макросов, но мы здесь говорим именно о макросах в качестве констант.
И в этом контексте правильнее использовать константы, а не макросы. А в некоторых случаях использование макросов в качестве констант может привести к путанице и скрытым багам.


Вывод: все хорошо в меру. Как я и говорил с самого начала.

87
23 сентября 2008 года
Kogrom
2.7K / / 02.02.2008
Цитата: Nixus
Я написал, что с помощью define могу сделать то что не могу с помощью const, ты спросил как - я показал. Что тебе еще нужно было, какой-то мухлеж выдумал.


Ок. Вероятно, выражение "1) Ну я согу сделать с define константой (обычной константой), то что ты не сомжешь сделать с const... Иногда это полезно и удобно." я должен был истолковывать как "define используется для создания макросов". Ну, тогда вопросов нет.

На счет строк я немного увлекся. Вероятно, действительно только строка char* создастся при компиляции.

Цитата: Nixus
Зачем сравнивать производительность вывода через cout и через printf?


Ты высказал сомнение по поводу эффективности кода при использовании STL. Поэтому я проверил, есть ли разница, если делать вывод сишными функциями.

87
23 сентября 2008 года
Kogrom
2.7K / / 02.02.2008
Цитата: Green
Конечно, такой способ лучше, чем приведенный Kogrom.


Почему лучше? Он не лучше, он другой. И у него тоже есть недостатки, о которых сам же Green говорил выше.

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

#define VERSION "1.111"
#define VERSION "1.2"
#define VERSION 3.14
#define VERSION 1024
#define VERSION "1.3"
#define VERSION "1.4"

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

3
23 сентября 2008 года
Green
4.8K / / 20.01.2000
Цитата: Nixus
Вывод: все хорошо в меру. Как я и говорил с самого начала.


Странный вывод...

G: микроскоп - не лучший молоток
N: Это догма, все зависит от обстоятельств. Например, микроскоп можно применить, как научный прибор
G: можно, но мы ж говорим о молотках
N: вывод: все хорошо в меру, как я и говорил

3
23 сентября 2008 года
Green
4.8K / / 20.01.2000
Цитата: Kogrom
Почему лучше? Он не лучше, он другой. И у него тоже есть недостатки, о которых сам же Green говорил выше.

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

#define VERSION "1.111"
#define VERSION "1.2"
#define VERSION 3.14
#define VERSION 1024
#define VERSION "1.3"
#define VERSION "1.4"

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


В том посте у меня есть слово "Но!", за ним следует расшифровка.
Приведенный Nixus-ом макрос используется не как константа, а как часть составного литерала.
Если мне нужно будет сконструировать строковый литерал (не строку), то я выберу такой способ.
Если же понадобится константа, тем более контекстно-зависимая, то это будет именно константа, а не макрос.

353
23 сентября 2008 года
Nixus
840 / / 04.01.2007
Цитата: Kogrom
Ок. Вероятно, выражение "1) Ну я согу сделать с define константой (обычной константой), то что ты не сомжешь сделать с const... Иногда это полезно и удобно." я должен был истолковывать как "define используется для создания макросов". Ну, тогда вопросов нет.


Под обычной я понимал что внутри define будет простой литерал, а не вычисление выражения или незаконченная конструкция языка. Ведь литерал можно определить и с помощью const. Но свойства будут разные.

Цитата: Kogrom
Ты высказал сомнение по поводу эффективности кода при использовании STL. Поэтому я проверил, есть ли разница, если делать вывод сишными функциями.


Я выразил сомнение в производительности string и map,а не cout и т.п., и то в том плане, что их аналоги можно сделать более быстрыми, как часто делают, например, в игровых приложениях. Взять хотябы известный Unreal Engine.

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог