Язык программирования C++. Пятое издание - Стенли Липпман
Шрифт:
Интервал:
Закладка:
У класса type_info нет стандартного конструктора, а оператор присвоения, конструктор копий и перемещения определены как удаленные (см. раздел 13.1.6). Поэтому нельзя определять, копировать или присваивать объекты типа type_info. Единственный способ создания объектов класса type_info — это оператор typeid.
Функция-член name() возвращает символьную строку в стиле С, содержащую имя класса объекта. Значение, используемое для данного типа, зависит от компилятора и не обязательно соответствует имени класса, использованному в программе. Единственное, что гарантирует функция name(), — это уникальность возвращаемой ей строки для данного типа.
Рассмотрим пример:
int arr[10];
Derived d;
Base *p = &d;
cout << typeid(42).name() << ", "
<< typeid(arr).name() << ", "
<< typeid(Sales_data).name() << ", "
<< typeid(std::string).name() << ", "
<< typeid(p).name() << " , "
<< typeid(*p).name() << endl;
При запуске на машине авторов эта программа выводит следующее
i, A10_i, 10Sales_data, Ss, P4Base, 7Derived
Класс type_info зависит от компилятора. Некоторые компиляторы предоставляют и другие функции-члены, которые возвращают дополнительную информацию о типах, используемых в программе. Чтобы выяснить реальные возможности класса type_info для конкретного компилятора, необходимо обратиться к его документации.
Упражнения раздела 19.2.4Упражнение 19.9. Напишите программу, подобную приведенной в конце этого раздела, для вывода имен, используемых компилятором для общих типов. Если ваш компилятор создает вывод, подобный нашему, напишите функцию, которая преобразует эти строки в более понятную для человека форму.
Упражнение 19.10. С учетом приведенной ниже иерархии классов, в которой каждый класс обладает открытым стандартным конструктором и виртуальным деструктором, укажите, какие имена типов отобразят следующие операторы?
class A { /* ... */ };
class В : public A { /* ... */ };
class С : public В { /* ... */ };
(a) A *pa = new С;
cout << typeid(pa).name() << endl;
(b) С cobj;
A& ra = cobj;
cout << typeid(&ra).name() << endl;
(c) B *px = new B;
A& ra = *px;
cout << typeid(ra).name() << endl;
19.3. Перечисления
Перечисления (enumeration) позволяют группировать наборы целочисленных констант. Как и класс, каждое перечисление определяет новый тип. Перечисления — литеральные типы (см. раздел 7.5.6).
В языке С++ есть два вида перечислений: с ограниченной и с не ограниченной областью видимости. Перечисление с ограниченной областью видимости (scoped enumeration) вводит новый стандарт. Для определения перечисления с ограниченной областью видимости используются ключевые слова enum class (или enum struct), сопровождаемые именем перечисления и разделяемым запятыми списком перечислителей (enumerator), заключенным в фигурные скобки. За закрывающей фигурной скобкой следует точка с запятой:
enum class open_modes {input, output, append};
Здесь определен тип перечисления open_modes с тремя перечислителями: input, output и append.
В определении перечисления с не ограниченной областью видимости (unscoped enumeration) ключевое слово class (или struct) отсутствует. Имя перечисления с не ограниченной областью видимости не является обязательным:
enum color {red, yellow, green}; // перечисление с не ограниченной
// областью видимости
// безымянное перечисление с не ограниченной областью видимости
enum {floatPrec = 6, doublePrec = 10, double_doublePrec = 10};
Если перечисление является безымянным, определить объекты его типа можно только в составе определения перечисления. Подобно определению класса, здесь можно предоставить разделяемый запятыми список объявлений между закрывающей фигурной скобкой и точкой с запятой, завершающей определение перечисления (см. раздел 2.6.1).
ПеречислителиИмена перечислителей в перечислении с ограниченной областью видимости подчиняются обычным правилам областей видимости и недоступны вне области видимости перечисления. Имена перечислителей в перечислении с не ограниченной областью видимости находятся в той же области видимости, что и само перечисление:
enum color {red, yellow, green}; // перечисление с не ограниченной
// областью видимости
enum stoplight {red, yellow, green}; // ошибка: переопределение
// перечислителей
enum class peppers {red, yellow, green}; // ok: перечислители
// скрываются
color eyes = green; // ok: перечислители находятся в области видимости
// для перечисления с не ограниченной областью видимости
peppers p = green; // ошибка: перечислители из peppers не находятся в
// области видимости
// color::green находится в области видимости,
// но имеет неправильный тип
color hair = color::red; // ok: к перечислителям можно обратиться явно
peppers p2 = peppers::red; // ok: использование red из peppers
По умолчанию значения перечислителей начинаются с 0, и значение каждого последующего перечислителя на 1 больше предыдущего. Однако вполне можно предоставить инициализаторы для одного или нескольких перечислителей:
enum class intTypes {
charTyp = 8, shortTyp = 16, intTyp = 16,
longTyp = 32, long_longTyp = 64
};
Как можно заметить на примере перечислителей intTyp и shortTyp, значение перечислителя не обязано быть уникальным. Без инициализатора значение перечислителя будет на 1 больше, чем у предыдущего.
Перечислители являются константами, и их инициализаторы должны быть константными выражениями (см. раздел 2.4.4). Следовательно, каждый перечислитель сам является константным выражением. Поскольку перечислители — константные выражения, их можно использовать там, где необходимы константные выражения. Например, можно определить переменные constexpr типа перечисления:
constexpr intTypes charbits = intTypes::charTyp;
Точно так же перечисление можно использовать как выражение в операторе switch, а значения его перечислителей как метки разделов case (см. раздел 5.3.2). По той же причине тип перечисления можно также использовать как параметр значения шаблона (см. раздел 16.1.1) и инициализировать статические переменные-члены типа перечисления в определении класса (см. раздел 7.6).
Подобно классам, перечисления определяют новые типыПоскольку перечисление имеет имя, можно определять и инициализировать объекты этого типа. Объект перечисления может быть инициализирован или присвоен только одному из своих перечислителей или другому объекту того же типа перечисления:
open_modes om = 2; // ошибка: 2 не имеет типа open_modes
om = open_modes::input; // ok: input - перечислитель open_modes
Объекты или перечислители типа перечисления с не ограниченной областью видимости автоматически преобразовываются в целочисленный тип. В результате они применимы там, где требуется целочисленное значение:
int i = color::red; // ok: перечислитель перечисления с не ограниченной
// областью видимости неявно преобразован в тип int
int j = peppers::red; // ошибка: перечисления с ограниченной областью
// видимости неявно не преобразуются
Определение размера перечисленияХотя каждое перечисление определяет уникальный тип, оно представляется одним из встроенных целочисленных типов. По новому стандарту можно указать, что следует использовать тип, заданный за именем перечисления и двоеточием:
enum intValues : unsigned long long {
charTyp = 255, shortTyp = 65535, intTyp = 65535,
longTyp = 4294967295UL,
long_longTyp = 18446744073709551615ULL
};
Если базовый тип не задан, то по умолчанию перечисления с ограниченной областью видимости имеют базовый тип int. Для перечислений с не ограниченной областью видимости типа по умолчанию нет; известно только то, что базовый тип достаточно велик для содержания значения перечислителя. Когда базовый тип определяется (включая неявное определение для перечисления с ограниченной областью видимости), попытка создания перечислителя, значение которого превосходит заданный тип, приведет к ошибке.