Итак, как же выразить идею о паре (int
позволяет хранить целые числа и выполнять операции сложения, вычитания, умножения и вычисления остатка, в то время как тип string
позволяет хранить последовательности символов и выполнять конкатенацию и доступ к символу по индексу. В языке С++ и его стандартной библиотеке определено много типов, например char
, int
, double
, string
, vector
и ostream
, но не тип Token
. На самом деле существует огромное количество типов — тысячи и сотни тысяч, — которые мы хотели бы иметь, но которых нет в языке и в стандартной библиотеке.
Среди наших любимых типов, которых нет в библиотеке, — классы Matrix
(см. главу 24), Date
(см. главу 9) и целые числа с бесконечной точностью (поищите в веб класс Bignum
). Если вы еще раз поразмыслите над этим, то поймете, что язык не может поддерживать десятки тысяч типов: кто их определит, кто их реализует, как их найти и какое толстое руководство по использованию языка при этом получится? Как и большинство современных языков программирования, язык С++ решает эту проблему, позволяя программисту при необходимости определять свои собственные типы (типы, определенные пользователем).
6.3.3. Реализация лексем
Как должна выглядеть лексема в нашей программе? Иначе говоря, как должен выглядеть тип Token
? Класс Token
должен предусматривать выполнение операторов, например +
и –
, а также представлять числа, такие как 42
и 3.14
. В самой простой реализации нужно придумать, как задать вид лексемы и как хранить числа.
Существует много способов реализации этой идеи в программе на языке С++. Вот ее простейший вариант:
class Token { // очень простой тип, определенный пользователем
public:
char kind;
double value;
};
Класс Token
— это тип (такой же, как int
или char
), поэтому его можно использовать для определения переменных и хранения значений. Он состоит из двух частей (kind
и value
. Ключевое слово class
означает “тип, определенный пользователем”; это значит, что он содержит члены (хотя в принципе может их и не содержать). Первый член, kind
, имеет тип char
и представляет собой символ. С его помощью удобно хранить символы '+'
и '*'
, чтобы представить операции *
и +
. Рассмотрим пример использования этого типа.
Token t; // t — объект класса Token
t.kind = '+'; // t представляет операцию +
Token t2; // t2 — другой объект класса Token
t2.kind = '8'; // цифра 8 означает, что "вид" является числом
t2.value = 3.14;
Для доступа к члену класса используется обозначение имя_объекта.имя_члена. Выражение t.kind
читается как “член kind
объекта t
”, а выражение t2.value
— как “член value
объекта t2
”. Объекты класса Token
можно копировать так же, как и переменные типа int
.
Token tt = t; // копирование при инициализации
if (tt.kind != t.kind) error("невозможно!");
t = t2; // присваивание
cout << t.value; // вывод числа 3.14
Имея класс Token
, можно выразить выражение (1.5+4)*11
с помощью семи лексем.
Обратите внимание на то, что для простых лексем значение не требуется, поэтому мы не используем член value
. Нам нужен символ для обозначения чисел. Мы выбрали символ '8'
просто потому, что он явно не оператор и не знак пунктуации. Использование символа '8'
для обозначения чисел немного загадочно, но это лишь на первых порах.
Класс Token
представляет пример типа, определенного пользователем. Тип, определенный пользователем, может иметь функции-члены (операции), а также данные члены. Существует много причин для определения функций-членов. В данном примере мы описали две функции-члена для того, чтобы инициализация объекта класса Token
стала проще.
class Token {
public:
char kind; // вид лексемы
double value; // для чисел: значение
Token(char ch) // создает объект класса Token
// из переменной типа char
:kind(ch), value(0) { }
Token(char ch, double val) // создает объект класса Token
:kind(ch), value(val) { } // из переменных типа