File: accessrights.yo

package info (click to toggle)
c%2B%2B-annotations 13.02.02-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 13,576 kB
  • sloc: cpp: 25,297; makefile: 1,523; ansic: 165; sh: 126; perl: 90; fortran: 27
file content (97 lines) | stat: -rw-r--r-- 4,710 bytes parent folder | download | duplicates (4)
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
Early in the annotations() (cf. section ref(HIDING)) we encountered two
important design principles when developing classes: em(data hiding) and
em(encapsulation).  Data hiding restricts control over an object's data to the
members of its class, encapsulation is used to restrict access to the
functionality of objects. Both principles are invaluable tools for maintaining
data integrity.

The keyword tt(private) starts sections in class interfaces in which members
are declared which can only be accessed by members of the class itself. This
is our main tool for realizing data hiding. According to established good
practices of class design the public sections are populated with member
functions offering a clean interface to the class's functionality. These
members allow users to communicate with objects; leaving it to the objects how
requests sent to objects are handled. In a well-designed class its objects are
in full control of their data.

Inheritance doesn't change these principles, nor does it change the way the
`tt(private)' and `tt(protected)' keywords operate. A derived class does not
have access to a base class's private section.

Sometimes this is a bit too restrictive. Consider a class implementing a
random number generating tt(streambuf) (cf. chapter ref(IOStreams)). Such a
tt(streambuf) can be used to construct an tt(istream irand), after which
extractions from tt(irand) produces series of random numbers, like in the next
example in which 10 random numbers are generated using stream I/O:
            verb(    RandBuf buffer;
    istream irand(&buffer);

    for (size_t idx = 0; idx != 10; ++idx)
    {
        size_t next;
        irand >> next;
        cout << "next random number: " << next << '\n';
    })

The question is, how many random numbers should tt(irand) be able to
generate? Fortunately, there's no need to answer this question, as
tt(RandBuf) can be made responsible for generating the next random
number. RandBuf, therefore, operates as follows:
        itemization(
        it() It generates a random number;
        it() It is passed in textual form to its base class tt(streambuf);
        it() The tt(istream) object extracts this random number, merely
            using tt(streambuf)'s interface;
        )
        (this process is repeated for subsequent random numbers).

    Once tt(RandBuf) has stored the text representation of the next
random number in some buffer, it must tell its base class (tt(streambuf))
where to find the random number's characters. For this tt(streambuf) offers a
member tt(setg), expecting the location and size of the buffer holding the
random number's characters.

    The member tt(setg) clearly cannot be declared in tt(streambuf)'s
private section, as tt(RandBuf) must use it to prepare for the
extraction of the next random number. But it should also not be in
tt(streambuf)'s public section, as that could easily result in unexpected
behavior by tt(irand). Consider the following hypothetical example:
        verb(    RandBuf randBuf;
    istream irand(&randBuf);

    char buffer[] = "12";
    randBuf.setg(buffer, ...);  // setg public: buffer now contains 12

    size_t next;
    irand >> next;              // not a *random* value, but 12.)

Clearly there is a close connection between tt(streambuf) and its derived
class tt(RandBuf). By allowing tt(RandBuf) to specify the buffer from
which tt(streambuf) reads characters tt(RandBuf) remains in control,
denying other parts of the program to break its well-defined behavior.

This close connection between base- and derived-classes is realized by a third
keyword related to the accessibility of class members: ti(protected). Here is
how the member tt(setg) could have been be declared in a class tt(streambuf):
        verb(    class streambuf
    {
        // private data here (as usual)
        protected:
            void setg(... parameters ...);  // available to derived classes

        public:
            // public members here
    };)

Protected members are members that can be accessed by derived classes, but are
not part of a class's public interface.

Avoid the temptation to declare em(data members) in a class's protected
section: it's a sure sign of bad class design as it needlessly results in
tight coupling of base and derived classes. The principle of i(data hiding)
should not be abandoned now that the keyword tt(protected) has been
introduced. If a derived class (but not other parts of the software) should
be given access to its base class's data, use member functions:
accessors and modifiers declared in the base class's protected
section. This enforces the intended restricted access without resulting in
tightly coupled classes.