inline Screen& Screen::up()
{ // переместить _cursor на одну строку вверх
// если уже наверху, остаться на месте и подать сигнал
if ( row() == 1 ) // наверху?
cout BELL endl;
else
_cursor -= _width;
return *this;
}
inline Screen& Screen::down()
{
if ( row() == _height ) //внизу?
cout BELL endl;
else
_cursor += _width;
return *this;
}
row() – это закрытая функция-член, которая используется в функциях up() и down(), возвращая номер строки, где находится курсор:
inline int Screen::row()
{ // вернуть текущую строку
return ( _cursor + _width ) / height;
}
Пользователи класса Screen попросили нас добавить функцию repeat(), которая повторяет указанное действие n раз. Ее реализация могла бы выглядеть так:
Screen &repeat( char op, int times )
{
switch( op ) {
case DOWN: // n раз вызвать Screen::down()
break;
case DOWN: // n раз вызвать Screen::up()
break;
// ...
}
}
Такая реализация имеет ряд недостатков. В частности, предполагается, что функции-члены класса Screen останутся неизменными, поэтому при добавлении или удалении функции-члена repeat() необходимо модифицировать. Вторая проблема – размер функции. Поскольку приходится проверять все возможные функции-члены, то исходный текст становится громоздким и неоправданно сложным.
В более общей реализации параметр op заменяется параметром типа указателя на функцию-член класса Screen. Теперь repeat() не должна сама устанавливать, какую операцию следует выполнить, и всю инструкцию switch можно удалить. Определение и использование указателей на члены класса – тема последующих подразделов.
13.6.1. Тип члена класса
Указателю на функцию нельзя присвоить адрес функции-члена, даже если типы возвращаемых значений и списки параметров полностью совпадают. Например, переменная pfi – это указатель на функцию без параметров, которая возвращает значение типа int:
int (*pfi)();
Если имеются глобальные функции HeightIs() и WidthIs() вида:
int HeightIs();
int WidthIs();
то допустимо присваивание pfi адреса любой из этих переменных:
pfi = HeightIs;
pfi = WidthIs;
В классе Screen также определены две функции доступа, height() и width(), не имеющие параметров и возвращающие значение типа int:
inline int Screen::height() { return _height; }
inline int Screen::width() { return _width; }
Однако попытка присвоить их переменной pfi является нарушением типизации и влечет ошибку компиляции:
// неверное присваивание: нарушение типизации
pfi = &Screen::height;
В чем нарушение? У функций-членов есть дополнительный атрибут типа, отсутствующий у функций, не являющихся членами, – класс. Указатель на функцию-член должен соответствовать типу присваиваемой ему функции не в двух, а в трех отношениях: по типу и количеству формальных параметров; типу возвращаемого значения; типу класса, членом которого является функция.
Несоответствие типов между двумя указателями – на функцию-член и на обычную функцию – обусловлено их разницей в представлении. В указателе на обычную функцию хранится ее адрес, который можно использовать для непосредственного вызова. (Указатели на функции рассматривались в разделе 7.9.) Указатель же на функцию-член должен быть сначала привязан к объекту или указателю на объект, чтобы получить this, и только после этого он применяется для вызова функции-члена. (В следующем подразделе мы покажем, как осуществить такую привязку.) Хотя для указателя на обычную функцию и для указателя на функцию-член используется один и тот же термин, их природа различна.
Синтаксис объявления указателя на функцию-член должен принимать во внимание тип класса. То же верно и в отношении указателей на данные-члены. Рассмотрим член _height класса Screen. Его полный тип таков: член класса Screen типа short. Следовательно, полный тип указателя на _height – это указатель на член класса Screen типа short:
short Screen::*
Определение указателя на член класса Screen типа short выглядит следующим образом:
short Screen::*ps_Screen;
Переменную ps_Screen можно инициализировать адресом _height:
short Screen::*ps_Screen = &Screen::_height;
или присвоить ей адрес _width:
short Screen::*ps_Screen = &Screen::_width;