File: using.yo

package info (click to toggle)
c%2B%2B-annotations 11.5.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 11,244 kB
  • sloc: cpp: 21,698; makefile: 1,505; ansic: 165; sh: 121; perl: 90
file content (152 lines) | stat: -rw-r--r-- 6,827 bytes parent folder | download | duplicates (2)
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
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.

    hi(pointer: to member field selector) 
To use a pointer to member in combination with an object the
    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 (in the next line) 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
    {
        std::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).

    Since the tt(EmployeeCategory) values are known, the tt(switch) in the
above constructor can also easily be avoided by defining a static array of
pointers to functions. The class tt(PersonData) defines the static array:
        verb(    class PersonData
    {
        std::string (PersonData::*d_infoPtr)(Person *p);
        static std::string (PersonData::*s_infoPtr[])(Person *p);
    };)

and tt(s_infoPtr[]) can be initialized compile-time:
        verb(    string (PersonData::*PersonData::s_infoPtr[])(Person *p) = 
    {
        &PersonData::allInfo,       // BOARD
        &PersonData::allInfo,       // SUPERVISOR
        &PersonData::noPhone,       // SALESPERSON
        &PersonData::nameOnly       // CLERK
    };)

The constructor, instead of using a tt(switch), now directly calls the
required member from the appropriate array element:
        verb(    PersonData::PersonData(PersonData::EmployeeCategory cat)
    :
        d_infoPtr(s_infoPtr[cat])
    {})

An example using pointers to data members is provided in section
ref(STABSORT), in the context of the tt(stable_sort) generic algorithm.