1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
Using pointers to members to call a member function requires the existence of
an object of the class of the members to which the pointer to member refers
to. With pointers operating at global scope, the dereferencing operator tt(*)
is used. With pointers to objects the i(field selector) operator operating on
pointers (ti(->)) or the field selector operating operating on objects (tt(.))
can be used to select appropriate members.
To use a pointer to member in combination with an object the
i(pointer to member field selector) (ti(.*)) must be specified. To use a
pointer to a member via a pointer to an object the `pointer to member field
selector through a pointer to an object' (ti(->*)) must be specified. These
two operators combine the notions of a field selection (the tt(.) and tt(->)
parts) to reach the appropriate field in an object and of dereferencing: a
dereference operation is used to reach the function or variable the pointer to
member points to.
Using the example from the previous section, let's see how we can use
pointers to member functions and pointers to data members:
verbinclude(-a examples/using.cc)
We note:
itemization(
it() At (1) a tt(PointerDemo) object and a pointer to such an
object is defined.
it() At (2) we specify an object (and hence the tt(.*) operator) to reach
the member tt(valuePtr) points to. This member is given a value.
it() At (3) the same member is assigned another value, but this time using
the pointer to a tt(PointerDemo) object. Hence we use the tt(->*) operator.
it() At (4) the tt(.*) and tt(->*) are used once again, this time to call
a function through a pointer to member. As the function argument list
has a higher priority than the pointer to member field selector
operator, the latter em(must) be protected by parentheses.
)
Pointers to members can be used profitably in situations where a class has
a member that behaves differently depending on a configuration
setting. Consider once again the class tt(Person) from section
ref(ASSIGNMENT). tt(Person) defines data members holding a person's name,
address and phone number. Assume we want to construct a tt(Person)
database of employees. The employee database can be queried, but depending on
the kind of person querying the database either the name, the name and phone
number or all stored information about the person is made available. This
implies that a member function like tt(address) must return something like
`tt(<not available>)' in cases where the person querying the database is not
allowed to see the person's address, and the actual address in other cases.
The employee database is opened specifying an argument reflecting the
status of the employee who wants to make some queries. The status could
reflect his or her position in the organization, like tt(BOARD),
tt(SUPERVISOR), tt(SALESPERSON), or tt(CLERK). The first two categories are
allowed to see all information about the employees, a tt(SALESPERSON) is
allowed to see the employee's phone numbers, while the tt(CLERK) is only
allowed to verify whether a person is actually a member of the organization.
We now construct a member tt(string personInfo(char const *name)) in the
database class. A standard implementation of this class could be:
verb(
string PersonData::personInfo(char const *name)
{
Person *p = lookup(name); // see if `name' exists
if (!p)
return "not found";
switch (d_category)
{
case BOARD:
case SUPERVISOR:
return allInfo(p);
case SALESPERSON:
return noPhone(p);
case CLERK:
return nameOnly(p);
}
}
)
Although it doesn't take much time, the tt(switch) must nonetheless be
evaluated every time tt(personInfo) is called. Instead of using a switch, we
could define a member tt(d_infoPtr) as a pointer to a member function of the
class tt(PersonData) returning a tt(string) and expecting a pointer to a
tt(Person) as its argument.
Instead of evaluating the switch this pointer can be used to point to
tt(allInfo), tt(noPhone) or tt(nameOnly). Furthermore, the member function
the pointer points to will be known by the time the tt(PersonData)
object is constructed and so its value needs to be determined only once (at
the PersonData object's construction time).
Having initialized tt(d_infoPtr) the tt(personInfo) member function is now
implemented simply as:
verb(
string PersonData::personInfo(char const *name)
{
Person *p = lookup(name); // see if `name' exists
return p ? (this->*d_infoPtr)(p) : "not found";
}
)
The member tt(d_infoPtr) is defined as follows (within the class
tt(PersonData), omitting other members):
verb(
class PersonData
{
string (PersonData::*d_infoPtr)(Person *p);
};
)
Finally, the constructor initializes tt(d_infoPtr). This could be realized
using a simple switch:
verb(
PersonData::PersonData(PersonData::EmployeeCategory cat)
:
switch (cat)
{
case BOARD:
case SUPERVISOR:
d_infoPtr = &PersonData::allInfo;
break;
case SALESPERSON:
d_infoPtr = &PersonData::noPhone;
break;
case CLERK:
d_infoPtr = &PersonData::nameOnly;
break;
}
}
)
Note how addresses of member functions are determined. The class
tt(PersonData) scope em(must) be specified, even though we're already inside
a member function of the class tt(PersonData).
An example using pointers to data members is provided in section
ref(STABSORT), in the context of the tt(stable_sort) generic algorithm.
|