Определите указатель на член и инициализируйте его значением Screen::_screen; присвойте ему значение Screen::_cursor.
Упражнение 13.13
Определите typedef для каждой из функций-членов класса Screen.
Упражнение 13.14
Указатели на члены можно также объявлять как данные-члены класса. Модифицируйте определение класса Screen так, чтобы оно содержало указатель на его функцию-член того же типа, что home() и end().
Упражнение 13.15
Модифицируйте имеющийся конструктор класса Screen (или напишите новый) так, чтобы он принимал параметр типа указателя на функцию-член класса Screen, для которой список формальных параметров и тип возвращаемого значения такие же, как у home() и end(). Реализуйте для этого параметра значение по умолчанию и используйте параметр для инициализации члена класса, описанного в упражнении 13.14. Напишите функцию-член Screen, позволяющую пользователю задать ее значение.
Упражнение 13.16
Определите перегруженный вариант repeat(), который принимает параметр типа cursorMovements.
13.7. Объединение – класс, экономящий память
Объединение – это специальный вид класса. Данные-члены хранятся в нем таким образом, что перекрывают друг друга. Все члены размещаются, начиная с одного и того же адреса. Для объединения отводится столько памяти, сколько необходимо для хранения самого большого его члена. В любой момент времени можно присвоить значение лишь одному такому члену.
Рассмотрим пример, иллюстрирующий использование объединения. Лексический анализатор, входящий в состав компилятора, разбивает программу на последовательность лексем. Так, инструкция
int i = 0;
преобразуется в последовательность из пяти лексем:
* Ключевое слово int.
* Идентификатор i.
* Оператор =
* Константа 0 типа int.
* Точка с запятой.
Лексический анализатор передает эти лексемы синтаксическому анализатору, парсеру, который идентифицирует полученную последовательность. Полученная информация должна дать парсеру возможность распознать эту последовательность лексем как объявление. Для этого с каждой лексемой ассоциируется информация, позволяющая парсеру увидеть следующее:
Type ID Assign Constant Semicolon
(Тип ИД Присваивание Константа Точка с запятой)
Далее парсер анализирует значения каждой лексемы. В данном случае он видит:
Type == int
ID == i
Constant == 0
Для Assign и Semicolon дополнительной информации не нужно, так как у них может быть только одно значение: соответственно := и ;
Таким образом, в представлении лексемы могло бы быть два члена – token и value. token – это уникальный код, показывающий, что лексема имеет тип Type, ID, Assign, Constant или Semicolon, например 85 для ID и 72 для Semicolon.value содержит конкретное значение лексемы. Так, для лексемы ID в предыдущем объявлении value будет содержать строку "i", а для лексемы Type – некоторое представление типа int.
Представление члена value несколько проблематично. Хотя для любой отдельной лексемы в нем хранится всего одно значение, их типы для разных лексем могут различаться. Для лексемы ID в value хранится строка символов, а для Constant – целое число.
Конечно, для хранения данных нескольких типов можно использовать класс. Разработчик компилятора может объявить, что value принадлежит к типу класса, в котором для каждого типа данных есть отдельный член.
Применение класса решает проблему представления value. Однако для любой данной лексемы value имеет лишь один из множества возможных типов и, следовательно, будет задействован только один член класса, хотя памяти выделяется столько, сколько нужно для хранения всех членов. Чтобы память резервировалась только для нужного в данный момент члена, применяется объединение. Вот как оно определяется:
union TokenValue {
char _cval;
int _ival;
char *_sval;
double _dval;
};
Если самым большим типом среди всех членов TokenValue является dval, то размер TokenValue будет равен размеру объекта типа double. По умолчанию члены объединения открыты. Имя объединения можно использовать в программе всюду, где допустимо имя класса:
// объект типа TokenValue
TokenValue last_token;
// указатель на объект типа TokenValue
TokenValue *pt = new TokenValue;
Обращение к членам объединения, как и к членам класса, производится с помощью операторов доступа:
last_token._ival = 97;
char ch = pt-_cval;
Члены объединения можно объявлять открытыми, закрытыми или защищенными:
union TokenValue {
public:
char _cval;
// ...
private:
int priv;
}
int main() {
TokenValue tp;
tp._cval = '\n'; // правильно
// ошибка: main() не может обращаться к закрытому члену
// TokenValue::priv