Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 31.05.2013, 23:21   #1
RussDragon
Форумчанин
 
Аватар для RussDragon
 
Регистрация: 07.04.2012
Сообщений: 216
По умолчанию Перечесление / enumiration

Дошел но enum'a, как инициализируется, объявляется - понял. Но так и не понял его предназначения, кроме как в if'ках, можете подсказать, где в основном используется перечисление?
RussDragon вне форума Ответить с цитированием
Старый 31.05.2013, 23:44   #2
Igor95
Форумчанин
 
Регистрация: 03.01.2013
Сообщений: 388
По умолчанию

enum используется для объявления и инициализации, связанных по смыслу, именованых констант...
Т.е, вместо:
const int Monday = 0;
const int Tuesdaay = 1;
...
можно записать
enum {Monday, Tuesday...};
Igor95 вне форума Ответить с цитированием
Старый 01.06.2013, 01:59   #3
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Цитата:
Сообщение от RussDragon Посмотреть сообщение
Дошел но enum'a, как инициализируется, объявляется - понял. Но так и не понял его предназначения, кроме как в if'ках, можете подсказать, где в основном используется перечисление?
Область применения:

1. Его можно использовать везде, где используется обычная интовая константа. Но в отличие от интовой константы, под значения перечислений гарантировано не будет выделена память.

Пример использования:

Код:
//память для хранения значений eONE и eTWO выделена не будет
enum eExample{ eONE, eTWO };

void Foo(const eExample arg) 
{
   switch(arg)
  {
     case eONE: { break; }
     case eTWO : { break; }
  }
}
В примере, arg имеет тип перечисления. Это значит, что без дополнительных полухакерских движений, переменная arg не сможет принимать никаких значений, за исключением значений eONE или eTWO

Это привентивно сокращает количество возможных ошибок в коде, а так же избавляет пользователя от необходимости выполнять проверку корректности значения arg

Последний раз редактировалось _Bers; 01.06.2013 в 02:26.
_Bers вне форума Ответить с цитированием
Старый 01.06.2013, 02:01   #4
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Другой частый случай использования:

Код:
//значения констант задаются вот по такому принципу:
    enum eCONFIGURATION
    {
        eFULLSCREEN = 1<<0,
        eVIDEO_MEMORY = 1<<1,
        eDEBUG_MODE = 1<<2 
        //всего их может быть 32
    };

...

//Сообщаем движку желаемые настройки
Engine engine(eFULLSCREEN |eVIDEO_MEMORY | eDEBUG_MODE );
Можно указывать только нужные вещи, и таким образом комбинировать настройки:

Код:
Engine engine(eFULLSCREEN|eVIDEO_MEMORY);
или

Код:
Engine engine(eFULLSCREEN|eDEBUG_MODE );
То есть, при помощи енумов очень удобно передавать различные настройки чего-либо, которые можно как угодно комбинировать.
_Bers вне форума Ответить с цитированием
Старый 01.06.2013, 02:03   #5
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Удобно работать с флагами состояний. Одна переменная типа int может заменить собой 32 булевые переменные:

Код:
#include<iostream>

class CFlag
{
    typedef unsigned int Flag32;
public:
    CFlag(Flag32 startFlag=0):mFlags(startFlag){}
   ~CFlag(){}
public:
    void Clear()                    { mFlags=0;                      } 
    Flag32 Get()const               { return mFlags;                 } 
    bool Is(const Flag32 flag)const { return (flag & mFlags)== flag; } 
    void Add(const Flag32 flag)     { mFlags |= flag;                } 
    void Del(const Flag32 flag)     { mFlags &= (~flag);             } 
    void Only(const Flag32 flag)    { Clear(); Add(flag);            }

    bool operator==(const Flag32 flag)const { return  Is(flag); }
    bool operator!=(const Flag32 flag)const { return !Is(flag); }
private:
    Flag32 mFlags;
};

//частная собственность единицы трансляции
namespace{

    //флаги создаются вот по такому принципу:
    enum eSTATE
    {
        eONE = 1<<0,
        eTWO = 1<<1,
        eFOO = 1<<2, //всего их может быть 32
    };
}

int main()
{
    //при создании, можно сразу указать множество включенных флагов
    CFlag flag(eONE|eTWO);

    //вот так проверяется, включен флаг, или нет. Выражение будет true, если включен
    if(flag==eONE) cout<<"eONE is on\n";
    if(flag==eTWO) cout<<"eTWO is on\n";
    if(flag==eFOO) cout<<"eFOO is on\n";

    //проверять можно сразу несколько флагов
    if(flag==(eONE|eTWO)) cout<<"eONE and eTWO is on\n";

    //выключает влаг
    flag.Del(eONE);

    //добавляет флаг к уже включенным
    flag.Add(eFOO);

    if(flag==eONE) cout<<"eONE is on\n";
    if(flag==eTWO) cout<<"eTWO is on\n";
    if(flag==eFOO) cout<<"eFOO is on\n";
    if(flag==(eFOO|eTWO)) cout<<"eFOO and eTWO is on\n";
    return 0;
}
Помимо удобного и читабельного способа управления многочисленными флагами состояния, не забываем так же и про то, что один инт занимает в памяти меньше памяти, чем 32 булевые переменные. Что во-первых экономит память, а во-вторых хорошо для кэша. То есть, чем меньше объекты жрут памяти, тем выше вероятность что массив таких объектов попадет в кэш. Теоретически, если "ужать" размер объекта, это может хорошо увеличить производительность.

Последний раз редактировалось _Bers; 01.06.2013 в 02:05.
_Bers вне форума Ответить с цитированием
Старый 01.06.2013, 02:09   #6
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Так же, енумы в силу своей природы (константы гарантированно времени компиляции) находят активное применение в метапрограммировании:

Для енума разрешается выполнять выражения:

enum eExample { eValue = выражение };

Но поскольку, енум - константа,гарантированно времени компиляции, то своё значение eValue получит тоже именно времени компиляции.

Это так же значит, что результат вычисления 'выражение' тоже должен быть времени компиляции.

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


Пример:

Код:
//---------вернет true, если типы одинаковые------//
template <typename T, typename U> struct IsEqual { enum { value = false }; };
template <typename T> struct IsEqual<T,T> { enum { value = true }; };
//------------------------------------------------//

//Теперь можно в коде делать проверки: одинаковые ли типы данных:
template<class T1, class T2> void Example(const T1&, constT2&)
{
    if(IsEqual<T1,T2>::value ) std::cout<<"типы одинаковые\n";
    else std::cout<<"типы не одинаковые\n";
}

int main()
{
   Example( int(), double() );
}
Расчеты времени компиляции экономят машинное время.
В рантайме тупо подставляется готовый заранее уже известный результат.


Другой пример:

Код:
#include <iostream>

//---------------проверяет, есть ли у класса-цели оператор нужной сигнатуры-----------------------
template<class T, class Signature> struct HasOperator
{
    typedef char (&No)[1]; typedef char (&Yes)[2];
    template<class U, Signature> struct Helper {};
    template<class U>static No HelpMe(...);
    template<class U>static Yes HelpMe(Helper<U, &U::operator() >* p);
    enum { value = sizeof(HelpMe<T>(0)) == sizeof(Yes) };
};

struct Example
{
   void operator()(){}
};

int main()
{
   //проверим, есть ли в классе Example метод:
   //void Example::operator()

   //задаем тип функции-члена, существование которой хотим проверить
   typedef void(Example::*Operator)();

   //enum, который существует внутри шаблона HasOperator
   //получит значение 1, если метод существует. 
   //А иначе - ноль
   const bool isOperator = HasOperator<Example,Operator>::value;

   if(isOperator) std::cout<<"в этом классе существует метод void operator()\n";
   else std::cout<<"в этом классе не существует метода void operator()\n";
   return 0;
}

Последний раз редактировалось _Bers; 01.06.2013 в 02:30.
_Bers вне форума Ответить с цитированием
Старый 01.06.2013, 16:54   #7
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

вот пример рекурсивного шаблона:

Код:
#include<iostream>

template<size_t NUM> struct Factorial
{
   enum { eValue = NUM* Factorial<NUM-1>::eValue };
};

template<> struct Factorial<1>
{
   enum { eValue = 1 };
};

int main()
{
   const size_t factorial = Factorial<10>::eValue;
   std::cout<< "factorial = "<<factorial<<std::endl;
}

Здесь енум получает значение равное произведению своего параметра на значение другого енума, который содержится внутри точно такого же шаблона:

Код:
template<size_t NUM> struct Factorial
{
   enum { eValue = NUM* Factorial<NUM-1>::eValue };
};
однако шаблона который участвует в выражении инстанцируется с параметром меньшим на единицу. Внутри которого точно такой же енум так же получает значение... и тд. Образуется рекурсия.

Рекурсия доходит до шаблона с параметром <1>:

Код:
//псевдокод
enum { eValue = NUM* Factorial<1>::eValue };
И тут уже срабатывает специализация шаблона под этот параметр, и рекурсия прекращается.

Если проилюстрировать процесс рекурсии в псевдокоде, то получится что-то вроде:

Код:
//псевдокод
enum { eValue = 10*(9*(8*(7*(6*5*(4*(3*(2*1))))))) };
Так можно вычислить факториал времени компиляции (но если задать слишком большое число - компилятор может подвиснуть. )
_Bers вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Перечесление из массивов. ATAMAN200 Общие вопросы C/C++ 4 18.11.2010 19:13