Можно брать адрес члена класса. Получение адреса функции члена часто бывает полезно, поскольку те цели и причины, кторые приводились в #4.6.9 относительно указателей на фунции, в равной степени применимы и к функциям членам. Однако, на настоящее время в языке имеется дефект: невозможно описать выражением тип указателя, который получается в результате этой операции. Поэтому в текущей реализации приходится жулничать, используя трюки. Что касается примера, который привдится ниже, то не гарантируется, что он будет работать. Ипользуемый трюк надо локализовать, чтобы программу можно было преобразовать с использованием соответствующей языковой контрукции, когда появится такая возможность. Этот трюк исползует тот факт, что в текущей реализации this реализуется как первый (скрытый) параметр функции члена*:
– * Более поздние версии С++ поддерживают понятие указтель на член: cl::* означает «указатель на член класса cl». Например:
typedef void (cl::*PROC)(int); PROC pf1 = amp;cl::print; // приведение к типу ненужно PROC pf2 = amp;cl::print;
Для вызовов через указатель на функцию член используются операции . и -». Например:
(z1.*pf1)(2); (( amp;z2)-»*pf2)(4);
(прим. автора)
#include «stream.h»
struct cl (* char* val; void print(int x) (* cout «„ val «« x «« «\n“; *); cl(char* v) (* val = v; *) *);
// ``фальшивый'' тип для функций членов: typedef void (*PROC)(void*, int);
main (* cl z1("z1 "); cl z2("z2 "); PROC pf1 = PROC( amp;z1.print); PROC pf2 = PROC( amp;z2.print); z1.print(1); (*pf1)( amp;z1,2); z2.print(3); (*pf2)( amp;z2,4); *)
Во многих случаях можно воспользоваться виртуальными функциями (см. Главу 7) там, где иначе пришлось бы использвать указатели на функции.
5.4.6 Структуры и объединения
По определению struct – это просто класс, все члены кторого открытые, то есть
struct s (* ...
есть просто сокращенная запись
class s (* public: ...
Структуры используются в тех случаях, когда сокрытие данных неуместно.
Именованное объединение определяется как struct, в котрой все члены имеют один и тот же адрес (см. #с.8.5.13). Если известно, что в каждый момент времени нужно только одно знчение из структуры, то объединение может сэкономить просранство. Например, можно определить объединение для хранения лексических символов C компилятора:
union tok_val (* char* p; // строка char v[8]; // идентификатор (максимум 8 char) long i; // целые значения double d; // значения с плавающей точкой *);
Сложность состоит в том, что компилятор, вообще говоря, не знает, какой член используется в каждый данный момент, пэтому надлежащая проверка типа невозможна. Например:
void strange(int i) (* tok_val x; if (i) x.p = "2"; else x.d = 2; sqrt(x.d); // ошибка если i != 0 *)
Кроме того, объединение, определенное так, как это, нельзя инициализировать. Например:
tok_val curr_val = 12; //ошибка:int присваивается tok_val'у
является недопустимым. Для того, чтобы это преодолеть, можно воспользоваться конструкторами:
union tok_val (* char* p; // строка char v[8]; // идентификатор (максимум 8 char) long i; // целые значения double d; // значения с плавающей точкой
tok_val(char*); // должна выбрать между p и v tok_val(int ii) (* i = ii; *) tok_val (* d = dd; *) *);
Это позволяет справляться с теми ситуациями, когда типы членов могут быть разрешены по правилам для перегрузки имени функции (см. #4.6.7 и #6.3.3). Например:
void f (* tok_val a = 10; // a.i = 10 tok_val b = 10.0; // b.d = 10.0 *)
Когда это невозможно (для таких типов, как char* и char[8], int и char, и т.п.), нужный член может быть найден только посредством анализа инициализатора в ходе выполнения или с помощью задания дополнительного параметра. Например:
tok_val::tok_val(char* pp) (* if (strlen(pp) «= 8) strncpy(v,pp,8); // короткая строка else p = pp; // длинная строка *)
Таких ситуаций вообще-то лучше избегать.
Использование конструкторов не предохраняет от такого случайного неправильного употребления tok_val, когда сначала
присваивается значение одного типа, а потом рассматривается как другой тип. Эта проблема решается встраиванием объединния в класс, который отслеживает, какого типа значение помщается:
class tok_val (* char tag; union (* char* p; char v[8]; long i; double d; *); int check(char t, char* s) (* if (tag!=t) (* error(s); return 0; *) return 1; *) public: tok_val(char* pp); tok_val(long ii) (* i=ii; tag='I'; *) tok_val(double dd) (* d=dd; tag='D'; *)
long amp; ival (* check('I',"ival"); return i; *) double amp; fval (* check('D',"fval"); return d; *) char* amp; sval (* check('S',"sval"); return p; *) char* id (* check('N',"id"); return v; *) *);
Конструктор, получающий строковый параметр, использует для копирования коротких строк strncpy. strncpy похожа на strcpy, но получает третий параметр, который указывает, сколько символов должно копироваться:
tok_val::tok_val(char* pp) (* if (strlen(pp) «= 8) (* // короткая строка tag = 'N' strncpy(v,pp,8); // скопировать 8 символов *) else (* // длинная строка tag = 'S'; p = pp; // просто сохранить указатель *) *)