Типы, не относящиеся к встроенным, называют типами, определенными пользователем (user-defined types — UDT). Они могут быть частью стандартной библиотеки, доступной в любой реализации языка С++ (например, классы
string
,
vector
и
ostream
, описанные в главе 10), или типами, самостоятельно созданными программистом, как классы
Token
и
Token_stream
(см. разделы 6.5 и 6.6). Как только мы освоим необходимые технические детали, мы создадим графические типы, такие как
Shape
,
Line
и
Text
(речь
о них пойдет в главе 13). Стандартные библиотечные типы являются такой же частью языка, как и встроенные типы, но мы все же рассматриваем их как определенные пользователем, поскольку они созданы из таких же элементарных конструкций и с помощью тех же приемов, как и типы, разработанные нами; разработчики стандартных библиотек не имеют особых привилегий и средств, которых нет у нас. Как и встроенные типы, большинство типов, определенных пользователем, описывают операции. Например, класс
vector
содержит операции
[]
и
size
(см. разделы 4.6.1 и В.4.8), класс
ostream
операцию
<<
, класс
Token_stream
операцию
get
(см. раздел 6.8), а класс
Shape
операции
add(Point)
и
set_color
(см. раздел 14.2).
Зачем мы создаем типы? Компилятор не знает всех типов, на основе которых мы хотим создавать свои программы. Это в принципе невозможно, поскольку существует слишком много полезных типов — ни один разработчик языка программирования или компиляторов не может знать обо всех. Каждый день мы разрабатываем новый тип. Почему? Какие типы можно признать хорошими? Типы являются хорошими, если они позволяют прямо отразить идею в коде. Когда мы пишем программу, нам хотелось бы непосредственно воплощать идеи в коде так, чтобы мы сами, наши коллеги и компилятор могли понять, что мы написали. Когда мы хотим выполнять арифметические операции над целыми числами, нам отлично подойдет тип
int;
когда хотим манипулировать текстом, класс
string
— хороший выбор; когда хотим манипулировать входной информацией для калькулятора, нам нужны классы
Token
и
Token_stream
. Необходимость этих классов имеет два аспекта.
• Представление. Тип “знает”, как представить данные, необходимые в объекте.
• Операции. Тип знает, какие операции можно применить к объектам.
Эту концепцию, лежащую в основе многих идей, можно выразить так: “нечто” имеет данные для представления своего текущего значения, — которое иногда называют текущим состоянием, — и набор операций, которые к ним можно применить. Подумайте о компьютерном файле, веб-странице, CD-плеере, чашке кофе, телефоне, телефонном справочнике; все они характеризуются определенными данными и имеют более или менее фиксированный набор операций, которые можно выполнить. В каждом случае результат операции зависит от данных — текущего состояния объекта.
Итак, мы хотим выразить “идею” или “понятие” в коде в виде структуры данных и набора функций. Возникает вопрос: “Как именно?” Ответ на этот вопрос изложен в данной главе, содержащей технические детали этого процесса в языке С++.
В языке С++ есть два вида типов, определенных пользователем: классы и перечисления. Классы носят намного более общий характер и играют более важную роль в программировании, поэтому мы сосредоточим свое внимание в первую очередь на них. Класс непосредственно выражает некое понятие в программе. Класс (class) — это тип, определенный пользователем. Он определяет, как представляются объекты этого класса, как они создаются, используются и уничтожаются (раздел 17.5). Если вы размышляете о чем-то как об отдельной сущности, то, вполне возможно, должны определить класс, представляющий эту “вещь” в вашей программе. Примерами являются вектор, матрица, поток ввода, строка,
быстрое преобразование Фурье, клапанный регулятор, рука робота, драйвер устройства, рисунок на экране, диалоговое окно, график, окно, термометр и часы.
В языке С++ (как и в большинстве современных языков) класс является основной строительной конструкцией в крупных программах, которая также весьма полезна для разработки небольших программ, как мы могли убедиться на примере калькулятора (см. главы 6 и 7).
9.2. Классы и члены класса
Класс — это тип, определенный пользователем. Он состоит из встроенных типов, других типов, определенных пользователем, и функций. Компоненты, использованные при определении класса, называются его членами (members). Класс может содержать несколько членов, а может и не иметь ни одного члена. Рассмотрим пример.
class X {
public:
int m; // данные - члены
int mf(int v) { int old = m; m=v; return old; } // функция - член
};
Члены класса могут иметь разные типы. Большинство из них являются либо данными-членами, определяющими представление объекта класса, либо функциями-членами, описывающими операции над такими объектами. Для доступа к членам класса используется синтаксическая конструкция вида объект.член. Например:
X var; // var — переменная типа X
var.m = 7; // присваиваем значение члену m объекта var
int x = var.mf(9); // вызываем функцию - член mf объекта var
Тип члена определяет, какие операции с ним можно выполнять. Например, можно считывать и записывать член типа
int
, вызывать функцию-член и т.д.
9.3. Интерфейс и реализация
Как правило, класс имеет интерфейс и реализацию. Интерфейс — это часть объявления класса, к которой пользователь имеет прямой доступ. Реализация — это часть объявления класса, доступ к которой пользователь может получить только с помощью интерфейса. Открытый интерфейс идентифицируется меткой
public:
, а реализация — меткой
private:
. Итак, объявление класса можно представить следующим образом:
class X { // класс имеет имя X
public:
// открытые члены:
// – пользовательский интерфейс (доступный всем)
// функции
// типы
// данные (лучше всего поместить в раздел private)
private:
// закрытые члены:
// – детали реализации (используется только членами
// данного класса)
// функции
// типы
// данные
};
Члены класса по умолчанию являются закрытыми. Иначе говоря, фрагмент