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

       

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


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

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(), которую мы обсуждали в начале этого раздела. Теперь она будет принимать указатель на функцию-член:

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;

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() можно применять в интерактивной программе, где пользователь выбирает вид перемещения курсора из отображаемого на экране меню.


Содержание раздела