На этом шаге мы рассмотрим особенности использования указателей на компоненты класса.
Две операции языка C++ '.*' и '->*' предназначены для работы с указателями на компоненты класса. Прежде чем объяснить их особенности, отметим, что указатель на компонент класса не является обычным указателем, унаследованным языком C++ от языка C. Обыкновенный указатель предназначен для адресации того или иного объекта (участка памяти) программы. Указатель на компонент класса не может адресовать никакого участка памяти, так как память выделяется не классу, а объектам этого класса при их создании. Таким образом, указатель на компонент класса при определении не адресует никакого конкретного объекта.
Каким же образом определяются (описываются) указатели на компоненты классов? Как эти указатели получают значения? Чем являются эти значения указателей? Какими возможностями обладают указатели на компоненты класса? Для каких целей они используются? Почему и как с указателями на компоненты класса используются две операции разыменования ('.*' и '->*')?
Компоненты класса, как уже многократно повторялось, делятся на две группы:
Указатели на компоненты класса по-разному определяются для данных и функций (методов) класса. Начнем с указателей на принадлежащие классу функции. Их определение имеет следующий формат:
тип_возвращаемого_функцией_значения (имя_класса::*имя_указателя_на_метод) (спецификация_параметров_функции);
Например, выше в классе complex ("комплексное число") определены методы (компонентные функции) double& re(), double& im(). Вне класса можно следующим образом описать указатель ptCom:
double& (complex::*ptCom) ();
Описав указатель ptCom на компонентные функции класса complex, можно почти обычным образом задать его значение:
ptCom = &complex::re; // "Настройка" указателя.
Теперь для любого объекта A класса complex
complex A(10.0,2.4); // Определение объекта A.
можно таким образом вызвать принадлежащую классу функцию re():
(A.*ptCom)() = 11.1; // Изменится вещественная часть A. cout << (A.*ptCom)(); // Вывод на печать A.real .
Изменив значение указателя
ptCom = &complex::im; // "Настройка" указателя.
можно с его помощью вызывать другую функцию того же класса:
cout << (A.*ptCom)(); // Вывод значения мнимой части A. complex B = A; // Определение нового объекта В. (B.*ptCom)() += 3.0; // Изменение значения мнимой части В.
В данных примерах определен и использован указатель на компонентную функцию без параметров, возвращающую значение типа double&. Его не удастся настроить на принадлежащие классу complex функции с другой сигнатурой и другим типом возвращаемого значения. Для обращения к компонентной функции display(), указатель ptDisp нужно ввести следующим образом:
void (complex::*ptDisp)(void);
Настроив указатель ptDisp на вещественную функцию display() класса complex, можно вызвать эту функцию для любого объекта этого класса:
ptDisp = &complex::display; // "Настройка" указателя. (B.*ptDisp)(); // Вызов функции display() для объекта B.
Формат определения указателя на компонентные данные класса:
тип_данных(имя_класса::*имя_указателя);
В определение указателя можно включить его явную инициализацию, используя адрес компонента:
&имя_класса::имя_компонента
При этом компонент класса должен быть общедоступным (public). Например, попытка определить и использовать указатель на длину строки (компонент int len) класса stroka:
int (stroka::*plen) = &stroka::len;
окажется неверной, так как компонент len класса stroka по умолчанию имеет атрибут private. В определенном на шаге 1 простейшем классе complex1 есть общедоступные компонентные данные типа double, поэтому следующее определение указателя pdat будет вполне правильным:
complex1 comp; comp.real = 16.0; comp.imag = 33.4; double (complex1::*pdat) = &complex1::comp.imag; cout << "\ncomp.imag = " << comp.*pdat; pdat = &complex1::comp.real; // Перестройка указателя. cout << "\ncomp.real = " << comp.*pdat;
Результат выполнения приведенных операторов:
comp.imag = 33.4 comp.real = 16
Указатель на компонент класса можно использовать в качестве фактического параметра при вызове функции.
В приведенных примерах мы использовали операцию '.*' разыменования указателей на компоненты класса:
имя_объекта.*указатель_на_компонент_данных имя_объекта.*указатель_на_метод(параметры)
Слева от операции '.*' кроме имени конкретного объекта может помещаться ссылка на объект.
Если определен указатель на объект класса и введены указатели на компоненты того же класса, то доступ к компонентам конкретных объектов можно получить с помощью бинарной операции '->*' доступа к компонентам класса через указатель на объект:
указатель_на_объект_класса->*указатель_на_компонент_данных указатель_на_объект_класса->*указатель_на_метод(параметры)
Первым (левым) операндом должен быть указатель на объект класса, значение которого - адрес объекта класса. Второй (правый) операнд - указатель на компонент класса. Результат выполнения операции '->*' - это либо компонент данных, либо компонентная функция класса. Например, определим и инициализируем указатель pcm1 на компонент данных класса complex1:
double (complex1::*pcml) = &complex1::real;
Определим и инициализируем указатель pcomplex1 на объекты класса complex1:
complex1 CM(10.2,-6.4); complex1 *pcomplex1 = &CM;
Теперь, применив операцию '->*', можно записать такое выражение:
pcomplex1->*pcm1 = 22.2;
Приведенный оператор присваивания изменяет значение вещественной части комплексного числа, представленного объектом CM класса complex1.
Если справа от операции '->*' находится инициализированный указатель на компонентную функцию для того объекта, на который "настроен" левый операнд, то выполнится обращение к соответствующему методу:
complex1 A(22.2,33.3) ; // Объект класса. complex1 *pComplex = &A; // Указатель класса. void (complex1::*pdisplay)(); // Указатель на компонентную // функцию. pdisplay = &complex1::display; // "Настройка" указателя. (pComplex->*pdisplay)(); // Вызов компонентной функции // через указатель на объект // класса и указатель на // компонентную функцию.
В данном примере на экран выводится сообщение:
real = 22.2, imag = 33.3
На следующем шаге мы рассмотрим определение компонентных функций (методов класса).