Приведем реализацию функции-члена repeat(), которую мы обсуждали в начале этого раздела. Теперь она будет принимать указатель на функцию-член:
typedef Screen& (Screen::Action)();
Screen& Screen::repeat( Action op, int times )
{
for ( int i = 0; i
Параметр op – это указатель на функцию-член, которая должна вызываться times раз.
Если бы нужно было задать значения аргументов по умолчанию, то объявление repeat() выглядело бы следующим образом:
class Screen {
public:
Screen &repeat( Action = &Screen::forward, int = 1 );
// ...
};
А ее вызовы так:
Screen myScreen;
myScreen.repeat(); // repeat( &Screen::forward, 1 );
myScreen.repeat( &Screen::down, 20 );
Определим таблицу указателей. В следующем примере Menu – это таблица указателей на функции-члены класса Screen, которые реализуют перемещение курсора. CursorMovements – перечисление, элементами которого являются номера в таблице Menu.
Action::Menu() = {
&Screen::home,
&Screen::forward,
&Screen::back,
&Screen::up,
&Screen::down,
&Screen::end
};
enum CursorMovements {
HOME, FORWARD, BACK, UP, DOWN, END
};
Можно определить перегруженную функцию-член move(), которая принимает параметр CursorMovements и использует таблицу Menu для вызова указанной функции-члена. Вот ее реализация:
Screen& Screen::move( CursorMovements cm )
{
( this-*Menu[ cm ] )();
return *this;
}
У оператора взятия индекса ([]) приоритет выше, чем у оператора указателя на функцию-член (-*). Первая инструкция в move() сначала по индексу выбирает из таблицы Menu нужную функцию-член, которая и вызывается с помощью указателя this и оператора указателя на функцию-член. move() можно применять в интерактивной программе, где пользователь выбирает вид перемещения курсора из отображаемого на экране меню.
13.6.3. Указатели на статические члены класса
Между указателями на статические и нестатические члены класса есть разница. Синтаксис указателя на член класса не используется для обращения к статическому члену. Статические члены – это глобальные объекты и функции, принадлежащие классу. Указатели на них – это обычные указатели. (Напомним, что статической функции-члену не передается указатель this.)
Объявление указателя на статический член класса выглядит так же, как и для указателя на объект, не являющийся членом класса. Для разыменования указателя никакой объект не требуется. Рассмотрим класс Account:
class Account {
public:
static void raiseInterest( double incr );
static double interest() { return _interestRate ; }
double amount() { return _amount; }
private:
static double _interestRate;
double _amount;
string _owner;
};
inline void Account::raiseInterest( double incr )
{
_interestRate += incr;
}
Тип &_interestRate – это double*:
// это неправильный тип для &_interestRate
double Account::*
Определение указателя на &_interestRate имеет вид:
// правильно: double*, а не double Account::*
double *pd = &Account::_interestRate;
Этот указатель разыменовывается так же, как и обычный, объект класса для этого не требуется:
Account unit;
// используется обычный оператор разыменования
double daily = *pd / 365 * unit._amount;
Однако, поскольку _interestRate и _amount – закрытые члены, необходимо иметь статическую функцию-член interest() и нестатическую amount().
Указатель на interest() – это обычный указатель на функцию:
// правильно
double (*)()
а не на функцию-член класса Account:
// неправильно
double (Account::*)()
Определение указателя и косвенный вызов interest() реализуются так же, как и для обычных указателей:
// правильно: double(*pf)(), а не double(Account::*pf)()
double(*pf)() = &Account::interest;
double daily = pf() / 365 * unit.amount();
Упражнение 13.11
К какому типу принадлежат члены _screen и _cursor класса Screen?
Упражнение 13.12