Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих (Стенли Липпман) 3-е хххх.pdf
Скачиваний:
84
Добавлен:
30.05.2015
Размер:
5.92 Mб
Скачать

С++ для начинающих

622

Использование typedef может облегчить чтение объявлений указателей на члены. Например, для типа указатель на функцию-член класса Screen без параметров, которая возвращает ссылку на объект Screen”, т.е.

Screen& (Screen::*)()

typedef Screen& (Screen::*Action)(); Action default = &Screen::home;

Следующий typedef определяет Action как альтернативное имя:

Action next = &Screen::forward;

Тип указатель на функцию-член можно использовать для объявления формальных параметров и типа возвращаемого значения функции. Для параметра того же типа можно также указать значение аргумента по умолчанию:

Screen& action( Screen&, Action)();

action() объявлена как принимающая два параметра: ссылку на объект класса Screen и указатель на функцию-член Screen без параметров, которая возвращает ссылку на его

Screen meScreen;

typedef Screen& (Screen::*Action)(); Action default = &Screen::home;

extern Screen& action( Screen&, Sction = &Screen::display );

void ff()

{

action( myScreen );

action( myScreen, default ); action( myScreen, &Screen::end );

объект. Вызвать action() можно любым из следующих способов:

}

В следующем подразделе обсуждается вызов функции-члена посредством указателя.

13.6.2. Работа с указателями на члены класса

К указателям на члены класса можно обращаться только с помощью конкретного объекта или указателя на объект типа класса. Для этого применяется любой из двух операторов доступа (.* для объектов класса и ссылок на них или ->* для указателей). Например, так вызывается функция-член через указатель на нее:

С++ для начинающих

623

 

int (Screen::*pmfi)() = &Screen::height;

 

 

 

 

Screen& (Screen::*pmfS)( const Screen& ) = &Screen::copy;

 

 

Screen myScreen, *bufScreen;

 

 

// прямой вызов функции-члена

 

 

if ( myScreen.height() == bufScreen->height() )

 

 

bufScreen->copy( myScreen );

 

 

// эквивалентный вызов по указателю

 

 

if ( (myScreen.*pmfi)() == (bufScreen->*pmfi)() )

 

 

(bufScreen->*pmfS)( myScreen );

 

 

 

 

(myScreen.*pmfi)()

 

 

 

 

 

Вызовы

 

 

(bufScreen->*pmfi)();

 

 

 

 

 

 

требуют скобок, поскольку приоритет оператора вызова () выше, чем приоритет взятия

 

указателя на функцию-член. Без скобок

 

 

myScreen.*pmfi()

 

 

 

 

интерпретируется как

 

 

myScreen.*(pmfi())

 

 

 

 

 

 

Это означает вызов функции pmfi() и привязку возвращенного ей значения к оператору

 

(.*). Разумеется, тип pmfi не поддерживает такого использования, так что компилятор

 

выдаст сообщение об ошибке.

 

 

typedef short Screen::*ps_Screen;

 

 

 

 

Screen myScreen, *tmpScreen = new Screen( 10, 10 );

 

 

ps_Screen pH = &Screen::_height;

 

 

ps_Screen pW = &Screen::_width;

 

 

tmpScreen->*pH = myScreen.*pH;

 

Указатели на данные-члены используются аналогично:

 

 

tmpScreen->*pW = myScreen.*pW;

 

 

 

 

 

 

Приведем реализацию функции-члена repeat(), которую мы обсуждали в начале этого

 

раздела. Теперь она будет принимать указатель на функцию-член:

 

С++ для начинающих

624

typedef Screen& (Screen::Action)();

Screen& Screen::repeat( Action op, int times )

{

for ( int i = 0; i < times; ++i ) (this->*op)();

return *this;

}

Параметр op это указатель на функцию-член, которая должна вызываться times раз.

Если бы нужно было задать значения аргументов по умолчанию, то объявление repeat()

class Screen { public:

Screen &repeat( Action = &Screen::forward, int = 1 ); // ...

выглядело бы следующим образом:

};

 

Screen myScreen;

// repeat( &Screen::forward, 1 );

myScreen.repeat();

А ее вызовы так:

myScreen.repeat( &Screen::down, 20 );

Определим таблицу указателей. В следующем примере Menu это таблица указателей на функции-члены класса Screen, которые реализуют перемещение курсора. CursorMovements перечисление, элементами которого являются номера в таблице

Action::Menu() = { &Screen::home, &Screen::forward, &Screen::back, &Screen::up, &Screen::down, &Screen::end

};

enum CursorMovements {

HOME, FORWARD, BACK, UP, DOWN, END

Menu.

};

Можно определить перегруженную функцию-член move(), которая принимает параметр CursorMovements и использует таблицу Menu для вызова указанной функции-члена. Вот ее реализация:

С++ для начинающих

625

Screen& Screen::move( CursorMovements cm )

{

( this->*Menu[ cm ] )(); return *this;

}

У оператора взятия индекса ([]) приоритет выше, чем у оператора указателя на функцию-член (->*). Первая инструкция в move() сначала по индексу выбирает из таблицы Menu нужную функцию-член, которая и вызывается с помощью указателя this и оператора указателя на функцию-член. move() можно применять в интерактивной программе, где пользователь выбирает вид перемещения курсора из отображаемого на экране меню.

13.6.3. Указатели на статические члены класса

Между указателями на статические и нестатические члены класса есть разница.

Синтаксис указателя на член класса не используется для обращения к статическому члену. Статические члены это глобальные объекты и функции, принадлежащие классу. Указатели на них это обычные указатели. (Напомним, что статической функции-члену не передается указатель this.)

Объявление указателя на статический член класса выглядит так же, как и для указателя на объект, не являющийся членом класса. Для разыменования указателя никакой объект

class Account { public:

static void raiseInterest( double incr );

static double interest() { return _interestRate ; } double amount() { return _amount; }

private:

_interestRate;

static double

double

_amount;

string

_owner;

};

 

inline void Account::raiseInterest( double incr )

{

_interestRate += incr;

не требуется. Рассмотрим класс Account:

}

// это неправильный тип для &_interestRate

Тип &_interestRate это double*: double Account::*

Определение указателя на &_interestRate имеет вид:

С++ для начинающих

626

// правильно: 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

Определите указатель на член и инициализируйте его значением Screen::_screen; присвойте ему значение Screen::_cursor.

Упражнение 13.13

Определите typedef для каждой из функций-членов класса Screen.

Упражнение 13.14