Перейти к содержанию

Последнее обновление:2026-10-12

C++ перечисления enum

Об авторе:

Личный официальный аккаунт:

wechat

Традиционное поведение

У перечислений есть следующие проблемы:

  • Неограниченная область видимости легко приводит к конфликтам имён. Например, следующий код не компилируется:

enum Color
{
    RED,
    BLUE
};
enum Feeling
{
    EXCITED,
    BLUE
};

int main()
{
    Color a = BLUE; // error
    Feeling b = EXCITED;
    std::cout << a << ":" << b << std::endl;
    return 0;
}
- Происходит неявное преобразование в int - Фактический тип, представляющий переменную перечисления, нельзя явно указать, из-за чего невозможно прямое (forward) объявление перечислимого типа.

Конкретная реализация: tradition_color.cpp

Классический подход

Простой способ решить проблему конфликтов имён, вызванную неограниченной областью видимости, — добавлять префикс к именам перечислителей: например, в примере выше заменить на COLOR_BLUE и FEELING_BLUE.

Как правило, ради единообразия добавляют префикс ко всем константам. Но код определения таких переменных перечисления выглядит громоздко. В программах на C, возможно, не обойтись без этого, однако программистам C++ такой способ вряд ли понравится. Альтернатива — пространства имён:

namespace Color 
{
    enum Type
    {
        RED=15,
        YELLOW,
        BLUE
    };
};

После этого можно определять новые переменные перечисления так: Color::Type c = Color::RED;. Если сделать using namespace Color, префикс можно вообще опустить, что упрощает код. Однако пространство имён впоследствии может пополняться новым содержимым, поэтому обеспечиваемая им закрытость области видимости невысока. В больших проектах всё же возможно, что разные люди дадут одинаковое имя разным перечислимым типам.

Более «эффективный» способ — ограничить область видимости классом или структурой; например: способ определения новых переменных такой же, как с пространством имён выше, но при этом не нужно беспокоиться, что содержимое класса изменят где-то ещё. Здесь используется структура, а не класс, потому что сами константы задуманы как общедоступные.

struct Color1
{
    enum Type
    {
        RED=102,
        YELLOW,
        BLUE
    };
};

Конкретная реализация: classic_practice.cpp

Перечисления-классы C++11

Описанный выше подход решает первую проблему, но бессилен перед двумя остальными. К счастью, в стандарт C++11 были введены «перечисления-классы» (enum class), которые неплохо решают упомянутые проблемы.

  • Область видимости нового enum больше не глобальная
  • Не может неявно преобразовываться в другие типы
/**
 * @brief C++11的枚举类
 * 下面等价于enum class Color2:int
 */
enum class Color2
{
    RED=2,
    YELLOW,
    BLUE
};
Color2 c2 = Color2::RED;
cout << static_cast<int>(c2) << endl; //必须转!
  • Можно указать конкретный тип для хранения enum
enum class Color3:char;  // 前向声明

// 定义
enum class Color3:char 
{
    RED='r',
    BLUE
};
char c3 = static_cast<char>(Color3::RED);

Конкретная реализация: classic_practice.cpp

Перечислимые типы в классе

Иногда мы хотим, чтобы определённые константы были действительны только внутри класса. Поскольку макроконстанты, определённые через #define, глобальны и не достигают этой цели, напрашивается идея использовать члены данных, модифицированные const. Однако члены данных const действительно существуют, но их смысл не тот, которого мы ожидаем.

Член данных const константен лишь в течение времени жизни конкретного объекта, а для всего класса он изменяем, поскольку класс может создавать множество объектов, и значения члена данных const в разных объектах могут различаться.

Нельзя инициализировать член данных const в объявлении класса. Следующее применение ошибочно, поскольку до создания объекта класса компилятор не знает, чему равен SIZE. (до стандарта C++11)

class A 
{
  const int SIZE = 100;   // 错误,企图在类声明中初始化 const 数据成员 
  int array[SIZE];  // 错误,未知的 SIZE 
}; 

Правильно делать это в списке инициализации конструктора класса:

class A 
{
  A(int size);  // 构造函数 
  const int SIZE ;    
}; 
A::A(int size) : SIZE(size)  // 构造函数的定义
{ 

} 
A  a(100); // 对象 a 的 SIZE 值为 100 
A  b(200); // 对象 b 的 SIZE 值为 200 

Как же создать константу, постоянную в рамках всего класса?

Не рассчитывайте на члены данных const — следует использовать перечислимые константы в классе. Например:

class Person{
public:
    typedef enum {
        BOY = 0,
        GIRL
    }SexType;
};
//访问的时候通过,Person::BOY或者Person::GIRL来进行访问。

Перечислимые константы не занимают памяти в объекте; они полностью вычисляются во время компиляции.

Недостаток перечислимых констант: их неявный тип данных — целочисленный, их максимальное значение ограничено, и они не могут представлять числа с плавающей точкой.

Похожие темы

Комментарии