2 семестр / Литература / Язык программирования С++. Краткий курс. Страуструп
.pdf2
• • • • • •
Пользовательские типы |
|
- |
Не паникуй! |
|
Дуглас Адамс |
Введение Структуры Классы Объединения Перечисления Советы
2.1.
Введение
Мы |
называем типы, |
которые могут быть построены |
из фундаментальных |
|||
типов |
(§1.4), модификатора |
const (§1.6) |
и операторов |
деклараторов |
(§1.7), |
|
встроенными типами. |
С++ |
имеет богатый |
набор встроенных типов и |
опера |
ций, который
сознательно
ограничен
низким
уровнем.
Они
непосредственно
и
эффективно
отражают
возможности
обычной
компьютерной
техники,
но
при
этом
не
предоставляют
программисту
средства
высокого
уровня
для
соз
дания
сложных
приложений.
Вместо
этого
С++
дополняет
встроенные
типы
и
операции
сложным
набором
механизмов
абстракции,
на
основе
которых
про
граммисты могут построить необходимые возможности высокого уровня.
Механизмы абстракции С++ разработаны прежде всего для того, чтобы
по
зволить
программистам
проектировать
и
реализовывать
их
собственные
типы
с
соответствующими
представлениями
и
операциями
и
изящно
использовать
такие
типы
в
своих
приложениях.
Типы,
построенные
из
других
типов
с
по
мощью
механизмов
абстракции
С++,
называют
пользовательскими
типами
(или
типами,
определенными
пользователями).
Таковыми
типами
являются
клас
сы
и
перечисленwt.
Пользовательские
типы
могут
быть
построены
как
из
встро
енных
типов,
так
и
из
других
пользовательских
типов.
Большая
часть
этой
книги
посвящена
дизайну,
реализации
и
использованию
пользовательских
ти
пов.
Пользовательские
типы
зачастую
оказываются
предпочтительнее
встро
енных,
потому
что
их
легче
использовать,
они
менее
подвержены
ошибкам
и
2.3.
Классы
45
Здесь представление вектора |
(члены elem и sz) доступно |
рез интерфейс, предоставляемый |
с помощью открытых членов: |
только че
Vector (),
operator
[]
()
и
size
().Пример
read
_
and
_
sum
()из
§2.2
упрощается:
douЫe read_and_sum(int s) |
|
|||
( |
|
|
|
|
Vector |
v(s); |
|
//Создание вектора |
|
for(int |
i=O; |
i!=v.size(); |
++i) |
|
cin>>v[i]; |
//Чтение |
элементов |
||
douЫe |
sum = |
О; |
|
|
for(int |
i=O; |
i!=v.size(); |
++i) |
|
sum+=v[i]; |
//Вычисление суммы |
|||
return |
sum; |
|
|
|
из |
s |
элементов |
элементов
"Функция"-член
с
тем
же
именем,
что
и
имя
класса,
называется
кон
структором, |
т.е. |
функцией,
используемой
для
конструирования
(создания)
объектов
класса.
Таким
образом,
конструктор
Vector
()
заменяет
функцию
vector
_
ini
t
()
из
§2.2. В
отличие
от
обычной
функции
конструктор
гаран
тированно используется для инициализации
образом, определение конструктора решает
объектов |
своего класса. Таким |
проблему |
неинициализирован |
ных
переменных
класса.
|
Vector (int) |
определяет, как будут создаваться |
|
В |
частности, он |
указывает, что для этого |
требуется |
число используется в качестве количества |
элементов. |
объекты типа Vector. целое число. Это целое Конструктор инициали
зирует
члены
Vector,
используя
список
инициализаторов
членов:
:elem{new
douЬle[s]
),
sz(s)
То есть сначала выполняется инициализация elem указателем на память для s элементов типа douЫe, полученную из свободной памяти. Затем выполня
ется инициализация sz значением s.
Обращение к элементам выполняется
с
помощью
функции
индекса,
име
нуемой
operator
[].
Она
возвращает
ссылку
на
соответствующий элемент
(douЬle&,
которая
позволяет
читать
и
записывать
этот
элемент).
Функция
size
()
возвращает
пользователям
количество
хранящихся эле
ментов. Очевидно,
что
обработка
ошибок
здесь
полностью
отсутствует,
но
мы
вер
немся к этому позже, в |
§3.5. Точно так |
же мы не |
предоставляем |
"возврата" в свободную |
память массива |
douЬle, |
полученного с |
механизм помощью
оператора
new;
в
§4.2.2
показано,
как
элегантно
это сделать с
использованием
деструктора. Между структурой
и
классом
нет
никакой
принципиальной
разницы;
struct
-
это
просто
класс с
членами,
открытыми
(puЫic)
по умолчанию.
46
Глава
2.
Пользовательские
типы
Например, |
вы |
структуры. |
|
можете
определить
конструкторы
и
другие
функции-члены
для
2.4.
Объединения
Объединение (union)
представляет
собой
структуру
(struct),
в
которой
все
члены
располагаются
по
одному
и
тому
же
адресу,
так
что
union
зани
мает
столько
же
памяти,
сколько
и
его
наибольший
член.
Естественно,
union
может
хранить
одновременно
значение
только
одного
члена.
Например,
рас
смотрим
запись
таблицы
символов,
которая
хранит
имя
и
значение.
Значение
может
иметь
тип
либо
Node*,
либо
int:
enum
Туре
{
ptr,
num
};
//
Туре
может
хранить
значения
ptr
и |
пит |
(§2.5)
struct Entry |
|
{ |
|
string |
name; |
Туре t; |
|
Node* |
р; |
int i; |
|
}; |
|
// |
string - |
тип |
стандартной |
библиотеки |
||
11 |
Используем |
р, |
если |
t==ptr |
|
|
11 |
Используем |
i, |
если |
t==num |
|
void
f{Entry*
ре)
if 11
(pe->t cout
==
<<
num) pe->i;
Члены
р
и |
i |
никогда
не
используются
одновременно,
так что
получается
пустая трата
нами union:
памяти.
Ее
легко
избежать,
указав,
что
оба
члена
являются
чле
union
Value
Node* |
р; |
|
int |
i; |
|
} ; |
|
|
Язык не отслеживает, |
какие |
|
должен делать программист : |
значения хранятся
в
объединении,
так что
это
struct Entry |
|
|
{ |
|
|
string name; |
||
Туре |
t; |
|
Value |
v; |
11 |
Используем
v.p,
если
t==ptr;
и
v.i,
если
t==num
);
2.5. |
Перечисления |
void |
f(Entry* |
ре) |
||
{ |
|
|
|
|
|
if |
(pe->t |
== |
num) |
|
|
cout |
<< |
pe->v.i; |
|
11 |
|
|
|
47
Поддержание
соответствия
между
полем
типа
(здесь
-
t)
и
типом,
содер
жащимся
в
объединении,
чревато ошибками.
Чтобы
избежать ошибок,
можно
обеспечить
это
соответствие,
инкапсулируя
объединение
и
поле
типа
в
класс
и
предлагая
доступ
только
через
функции-члены,
которые
гарантируют
кор
ректное
использование
объединения.
На
уровне
приложений
абстракции,
основанные
на
таких
маркированных
объединениях
(tagged unions),
являют
ся достаточно распространенными и полезными. |
Использование |
же "голых" |
|||||
объединений лучше |
всего свести к минимуму. |
|
|
|
|
||
Тип |
стандартной |
библиотеки variant |
может |
использоваться |
|
для устра |
|
нения |
большинства |
прямых применений |
объединений. |
variant |
сохраняет |
||
значение одного из |
множества альтернативных |
типов |
(§13.5.1). |
|
Например, |
variant<Node*, int> может содержать либо Node*, либо int.
С помощью variant пример Entry может быть записан следующим
об
разом:
struct Entry { string name; variant<Node*,int> };
v;
void |
f(Entry* |
ре) |
|
|
if |
(holds_alternative<int>(pe->v)) |
|
|
|
cout |
<< get<int>(pe->v); |
|
11 |
|
|
// *ре хранит int? (§13.5.1)
//Получаем этот int
Для
множества
применений
variant проще
и безопаснее,
чем
union.
2.5.
Перечисления
В дополнение к классам С++ поддерживает простую разновидность зовательских типов, для которой мы можем перечислить значения:
поль
enum enum
class class
Color { red, Traffic_light
Ыuе, green };
{ green, yellow,
red
};
Color col = Color::red; Traffic_light light = Traffic
light::red;
2.6.
Советы
49
Если
вы
не
хотите
явно
квалифицировать
имена
перечислителей
и
хотите,
чтобы
их
значения
были
целыми
числами
(без
необходимости
явного
преоб
разования),
можете
удалить
слово
class
из
enum
class
и
получить "обыч
ный"
enum.
Перечислители
такого
"обычного"
enum
находятся
в
той
же
об
ласти
видимости,
что
и
имя
их
перечисления,
и
неявно
преобразуются
в
свои
целые
значения.
Например:
enum int
Color col =
{ red, green;
green,
Ыuе
1;
Здесь
col
получает
значение
1. По
умолчанию
целочисленные
значения
счет
чиков
начинаются
с
О
и
увеличиваются
на единицу
для
каждого
очередного
перечислителя.
"Обычное"
перечисление
было
в
С++
(и
С)
с
самых
первых
дней,
так
что
несмотря
на то,
что
они
менее
хорошо
себя
ведут,
они
широко
распространены
в современном коде.
2.6.
Советы
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
Предпочитайте хорошо определенные |
пользовательские типы |
встроен |
|
ным, если последние оказываются слишком низкоуровневыми; |
§2.1. |
||
Организовывайте связанные данные в |
структуры (struct или |
class); |
|
§2.2; [CG:C. l ]. |
|
|
|
Представляйте различие между интерфейсом и |
реализацией с помощью |
||
class; §2.3; [CG:C.3]. |
|
|
|
struct представляет собой просто class, все |
члены которого |
по умол |
|
чанию являются puЫic; §2.3. |
|
|
|
Определите конструкторы для гарантии и простоты инициализации |
|||
классов; §2.3; [CG:C.2). |
|
|
|
Избегайте "голых" объединений; заворачивайте их в класс вместе с по |
|||
лем типа; §2.4; [CG:C.181 ]. |
|
|
|
Используйте перечисления для представления |
множеств именованных |
||
констант; §2.5; [CG:Enum.2]. |
|
|
|
Предпочитайте перечисления class |
enum "обычным" перечислениям |
||
enum для минимизации неожиданностей; §2.5; [CG:Enum.3]. |
|
||
Определите операции над перечислениями для безопасного и простого |
|||
использования; §2.5; [CG:Enum.4]. |
|
|
|