- •Ооп: Лекция 5. Вспомогательные средства для создания классов
- •Статические переменные-члены
- •Статические функции-члены
- •Приемы применения статических членов — разделяемый ресурс
- •Приемы применения статических членов — Factory Method
- •Приемы применения статических членов — Singleton
- •Физическое и логическое постоянство
- •Указатели на члены
- •Полные примеры из лекции
Указатели на члены
Указатели на члены похожи на указатели на обычные функции, в том смысле, что позволяют вызвать функцию-член или использовать переменную-член не зная его имени используя адрес совместимого типа. Однако дополнительно к такой возможности, указатели на члены манипулируют содержимым объектов классов.
Указатели на члены включают указатели на переменные-члены и указатели на функции-члены. Указатели на переменные-члены с физической точки зрения представляют собой смещение адреса относительно начала объекта. Указатели на функции-члены, как минимум, хранят адрес начала машинных инструкций в памяти, также как обычные указатели на функции, однако могут иметь дополнительные данные для более сложных видов классов (например, использующих виртуальные функции, множественное и виртуальное наследование).
Предположим имеется структура, описывающая точку с двумя действительными координатами:
struct Point
{
double m_x;
double m_y;
};
Представляется возможным объявить указатель на переменную-член данной структуры, и инициализировать его адресом первого или второго поля в зависимости от некоторого условия. Затем возможно непрямое использование содержимого объекта через сформированный указатель. При использовании обязательно необходимо указать объект, к которому применяется указатель на член. Доступ к самому объекту может быть как по значению или ссылки, так и через указатель:
// Определение указателя на переменную-член структуры Point типа double
double Point::* pCoordinate = ( какое-либо условие ) ?
& Point::m_x // В этой ветке инициализируем адресом поля m_x
: & Point::m_y ; // А в этой ветке — адресом поля m_y
// Модифицируем одно из полей, используя объект и указатель на переменную-член
Point p;
( p .* pCoordinate ) = 5;
// Читаем одно из полей, используя указатель на объект и указатель на переменную-член
Point * ptr = & p;
std::cout << ( ptr ->* pCoordinate );
Разумеется, нельзя взять указатель на член, если не совпадают типы данных указателя и поля.
Аналогично, можно воспользоваться указателями на функции-члены. Они декларируются подобно указателям на обычные функции, но дополнительно содержат имя класса и оператор разрешения области видимости (::) перед символом '*'. Для демонстрации данной конструкции языка преобразуем структуру-точку в класс и введем методы доступа:
class Point
{
double m_x;
double m_y;
public:
Point ( double _x, double _y );
double GetX () const { return m_x; }
double GetY () const { return m_y; }
void SetX ( double _x ) { m_x = _x; }
void SetY ( double _y ) { m_y = _y; }
};
Объявим два различных указателя на функции-член для соответствующих совместимых по интерфейсу пар методов GetX/GetY и SetX/SetY. Сделаем выбор в пользу конкретной координаты по некоторому условию, и воспользуемся объектом-точкой вместе со сформированными указателями для увеличения координаты на значение 2.5, а затем восстановим прежнее значение через указатель на объект-точку:
int main ()
{
// Указатель на метод из Point, возвращает double, // ничего не принимает, не меняет объект
double ( Point ::* pGetMethod ) () const;
// Указатель на метод из Point, не возвращает, // принимает аргумент double, меняет объект
void ( Point ::* pSetMethod ) ( double );
// Выбираем ту или иную координату для работы по некоторому произвольному условию.
// Для этого присваиваем указателям на методы адреса соответствующих методов
if ( некоторое условие )
{
pGetMethod = & Point::GetX;
pSetMethod = & Point::SetX;
}
else
{
pGetMethod = & Point::GetY;
pSetMethod = & Point::SetY;
}
// Создаем объект-точку
Point p( 3.5, 4.5 );
// Извлекаем текущую координату через объект p и указатель на метод
double currentCoordinate = ( p .* pGetMethod )();
// Обновляем текущую координату через объект p и указатель на метод
( p .* pSetMethod ) ( currentCoordinate + 2.5 );
// Берем адрес объекта
Point * ptr = & p;
// Извлекаем текущую координату через указатель на объект ptr и указатель на метод
currentCoordinate = ( ptr ->* pGetMethod ) ();
// Обновляем текущую координату через указатель на объект ptr и указатель на метод
( ptr ->* pSetMethod )( currentCoordinate – 2.5 );
}
Синтаксис объявления и использования существенно упрощается, если речь идет о стыковке со статическими членами. В этих случаях не происходит передачи адреса объекта, поэтому указатель на статическую функцию-член является обычным указателем на функцию, а указатель на статическую переменную-член — обычным указателем на глобальную переменную.
class A
{
public:
// Статическая переменная-член
static int ms_data;
// Статическая функция-член
static int GetData () { return ms_data; }
};
// Определение статического члена
int A::ms_data;
int main ()
{
// Использование указателя на статическую переменную-член класса
const int * pData = & A::ms_data;
std::cout << * pData;
// Использование указателя на статическую функцию-член класса
int ( * pStaticMethod )() = & A::GetData;
std::cout << ( * pStaticMethod )();
}
