Категории
Самые читаемые
onlinekniga.com » Компьютеры и Интернет » Программирование » Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Читать онлайн Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 58 59 60 61 62 63 64 65 66 ... 337
Перейти на страницу:
и большинство современных языков программирования, язык С++ решает эту проблему, позволяя программисту при необходимости определять свои собственные типы (типы, определенные пользователем).

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) { }  // из переменных типа

                               // char и double

};

Эти две функции-члена называют конструкторами (constructors). Их имя совпадает с именем типа, и они используются для инициализации (конструирования) объектов класса Token. Рассмотрим пример.

Token t1('+');      // инициализируем t1, так что t1.kind = '+'

Token t2('8',11.5); // инициализируем t2,

                    // так что t2.kind = '8' и t2.value = 11.5

В первом конструкторе фрагмент :kind(ch), value(0) означает “инициализировать член kind значением переменной ch и установить член value равным нулю”. Во втором конструкторе фрагмент :kind(ch), value(val) означает “инициализировать член kind значением переменной ch и установить член value равным переменной val”. В обоих вариантах нам требуется лишь создать объект класса Token, поэтому тело функции ничего не содержит: { }. Специальный синтаксис инициализации (список инициализации членов класса) начинается с двоеточия и используется только в конструкторах.

Обратите внимание на то, что конструктор не возвращает никаких значений, потому что в конструкторе это не предусмотрено. (Подробности изложены в разделах 9.4.2 и 9.7.)

6.3.4. Использование лексем

Итак, похоже, что мы можем завершить нашу программу, имитирующую калькулятор! Однако следует уделить немного времени для планирования. Как использовать класс Token в калькуляторе?

Можно считать входную информацию в вектор объектов Token.

Token get_token(); // считывает объекты класса Token из потока cin

vector<Token> tok; // здесь храним объекты класса Token

int main()

{

  while (cin) {

    Token t = get_token();

    tok.push_back(t);

  }

  // ...

}

Теперь можно сначала считать выражение, а вычислить его позднее. Например, для выражения 11*12 получим следующие лексемы:

Эти лексемы можно использовать для поиска операции умножения и ее операндов. Это облегчает выполнение умножения, поскольку числа 11 и 12 хранятся как числовые значения, а не как строки.

Рассмотрим теперь более сложные выражения. Выражение 1+2*3 состоит из пяти объектов класса Token.

Теперь операцию умножения можно выполнить с помощью простого цикла.

for (int i = 0; i<tok.size(); ++i) {

  if (tok[i].kind=='*') { // мы нашли умножение!

    double d = tok[i–1].value*tok[i+1].value;

    // и что теперь?

  }

}

Да, и что теперь? Что делать с произведением d? Как определить порядок выполнения частичных выражений? Хорошо, символ + предшествует символу *, поэтому мы не можем выполнить операции просто слева направо. Можно попытаться выполнить их справа налево! Этот подход сработает для выражения 1+2*3, но не для выражения 1*2+3. Рассмотрим выражение 1+2*3+4. Это пример “внутренних вычислений”: 1+(2*3)+4. А как обработать скобки? Похоже, мы зашли в тупик. Теперь необходимо вернуться назад, прекратить на время программировать и подумать о том, как считывается и интерпретируется входная строка и как вычисляется арифметическое выражение.

 

1 ... 58 59 60 61 62 63 64 65 66 ... 337
Перейти на страницу:
На этой странице вы можете бесплатно читать книгу Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп.
Комментарии