C++. Бархатный путь. Часть 2 - Указатели на компоненты класса. Доступ по указателю

ОГЛАВЛЕНИЕ

 

Указатели на компоненты класса. Доступ по указателю

Прежде всего, рассмотрим объявление класса XXX.

class XXX
{
public:
long x1;
int x2;
/*Данные-члены класса.*/
long getVal1() {return x1;}
long getVal2() {return x2*x1;}
/*Функции-члены класса без параметров.*/
int getVal3(int param) {return x2*param;}
char* getVal4(char *str) {return str;}
/*Функции-члены класса с параметрами.*/
static int f1() {return 100;}
static int f2() {return 10;}
static int f3(int param) {return param;}
/* Определение различных статических функций*/
XXX(long val1, int val2){x1 = val1; x2 = val2;}
/*Конструктор.*/
};

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

Для объявления указателя на нестатическую функцию используется специальная синтаксическая конструкция, состоящая из спецификатора объявления и заключённого в скобки квалифицированного имени указателя, состоящего из имени класса, операции доступа к члену класса ::, разделителя * , собственно имени указателя, закрывающей скобки и списка параметров:

int (XXX::*fp_3) (int);

Подобный указатель может быть проинициализирован инициализатором, состоящим из операции присвоения, операции взятия адреса и квалифицированного имени соответствующей функции-члена:

int (XXX::*fp_3) (int) = &XXX::getVal1;

Вот и нашлась достойная область применения квалифицированным именам.

Как известно, значение унарного выражения, состоящего из операции взятия и операнда, который является именем функции и первичного выражения, состоящего из имени функции эквивалентны. Это адрес данной функции. Поэтому поэтому в качестве инициализирующего выражения для указателя на функцию-член класса также может быть использовано первичное выражение, представляющее собой квалифицированное имя функции-члена:

Fp_3 = XXX::getVal2

Класс - это не объект! И не совсем понятно, какое значение имеет адрес нестатичесного члена класса. Значение проинициализированного указателя на нестатическую компоненту остаётся неопределённым.

Оно определяется лишь в результате выполнения операций обращения к членам класса .* и ->* .

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

int val = (q.*fp)(6);
char val = (pq->*fp4)("new string");

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

long (XXX::*px1) = &XXX::x1;
// Определение и инициализация указателя на член класса XXX типа long
q.*px11 = 10; // p - объект-представитель класса XXX.
pq->*px11 = 10;
// pq - указатель на объект-представитель класса XXX.

Основные приёмы работы с указателями на функции-члены демонстрируются на следующих примерах:

class XXX
{
public:
long x1;
int x2;
/*Данные-члены класса.*/
long getVal1() {return x1;}
long getVal2() {return x2*x1;}
/*Функции-члены класса без параметров.*/
int getVal3(int param) {return x2*param;}
char* getVal4(char *str) {return str;}
/*Функции-члены класса с параметрами.*/
static int f1() {return 100;}
static int f2() {return 10;}
static int f3(int param) {return param;}
/* Определение различных статических функций*/
XXX(long val1, int val2){x1 = val1; x2 = val2;}
/*Конструктор.*/
};
void main()
{
XXX q(1,2);/* Определение объекта.*/
XXX* pq = new (XXX);
pq->x1 = 100;
pq->x2 = 100;
/*Определение и инициализация объекта по указателю.*/
long (XXX::*fp_0) ();
/*Указатель на функцию-член класса.*/
long (XXX::*fp_1) () = &XXX::getVal1;
/*
Проинициализированный указатель на функцию-член класса. Его
значение является относительной величиной и равняется значению
смещения функции-члена относительно первого члена класса.
*/
fp_0 = XXX::getVal1;
/*
Инициализация первого указателя. Один и тот же указатель можно
настраивать на различные функции-члены класса. Главное, чтобы у
всех этих функций-членов совпадали списки параметров и возвращаемые
значения функций.
*/
long val_1 = (q.*fp1)();
/*Вызов функции-члена класса по указателю из объекта.*/

long val_2 = (pq->*fp0)();
/*
Вызов функции-члена класса по указателю с помощью указателя на объект.
*/
int (XXX::*fp_3) (int) = &XXX::getVal3;
/*
Проинициализированный указатель на функцию-член класса. С параметрами
типа int.
*/
int val_3 = (q.*fp_3)(6);
/*
Вызов функции-члена класса по указателю из объекта с передачей параметров.
*/
char* (XXX::*fp_4) (char) = &XXX::getVal3;
/*
Проинициализированный указатель на функцию-член класса с параметрами типа int.
*/
char val_4 = (pq->*fp4)("new string");
/*
Вызов функции-члена класса по указателю с помощью указателя на объект.
*/
int (*fp_5) () = &XXX::f1;
/*
Указатель на статическую функцию объявляется без спецификации класса.
Явная спецификация класса необходима лишь при инициализации указателя.
*/
int retval = (*fp_5)();
/*Вызов статической функции по указателю.*/
fp_5 = XXX::f2;
/*
Перенастройка статического указателя. Главное требование - совпадение
списков параметров и типа возвращаемого значения.
*/
int (*fp_6) (int) = &XXX::f3;
/*Указатель на статическую функцию с параметрами.*/
int retval = (*fp_6)(255);
/*Вызов статической функции с параметрами по указателю.*/
long (XXX::*px1) = &XXX::x1;
/*Определили и проинициализировали указатель на член класса long*/
q.*px11 = 10;
/*Используя указатель на компоненту класса, изменили значение переменной
x1 объекта q, представляющего класс XXX. */
pq->*px11 = 10;
/*Используя указатель на компоненту класса, изменили значение переменной
x1 объекта, представляющего класс XXX и расположенного по адресу pq. */
}

Вызов статических функций-членов класса не требует никаких объектов и указателей на объекты. От обычных функций их отличает лишь специфическая область видимости.