File: redefining.yo

package info (click to toggle)
c%2B%2B-annotations 10.6.0-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 10,536 kB
  • ctags: 3,247
  • sloc: cpp: 19,157; makefile: 1,521; ansic: 165; sh: 128; perl: 90
file content (175 lines) | stat: -rw-r--r-- 6,595 bytes parent folder | download
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
Derived classes may redefine base class members.  Let's assume that a vehicle
classification system must also cover trucks, consisting of two parts: the
front part, the tractor, pulls the rear part, the trailer. Both the
tractor and the trailer have their own mass, and the tt(mass) function
should return the combined mass.

The definition of a tt(Truck) starts with a class definition. Our initial
tt(Truck) class is derived from tt(Car) but it is then expanded to hold one
more tt(size_t) field representing the additional mass information. Here we
choose to represent the mass of the tractor in the tt(Car) class and to store
the mass of of full truck (tractor + trailer) in its own tt(d_mass) data
member: 
        verb(
    class Truck: public Car
    {
        size_t d_mass;

        public:
            Truck();
            Truck(size_t tractor_mass, size_t speed, char const *name,
                  size_t trailer_mass);

            void setMass(size_t tractor_mass, size_t trailer_mass);
            size_t mass() const;
    };

    Truck::Truck(size_t tractor_mass, size_t speed, char const *name,
                 size_t trailer_mass)
    :
        Car(tractor_mass, speed, name)
    {
        d_mass = trailer_mass + trailer_mass;
    }
        )
    Note that the class tt(Truck) now contains two functions already
present in the base class tt(Car): tt(setMass) and tt(mass).
    itemization(
    it() The redefinition of tt(setMass) poses no problems: this
function is simply redefined to perform actions which are specific to a
tt(Truck) object.
    it() Redefining tt(setMass), however,
    hi(hiding: base class  members) hi(base class: hiding members) em(hides)
tt(Car::setMass). For a tt(Truck) only the tt(setMass) function having
two tt(size_t) arguments can be used.
    it() The tt(Vehicle)'s tt(setMass) function remains available for a
tt(Truck), but it must now be
 hi(member function: called explicitly) called em(explicitly), as
tt(Car::setMass) is hidden from view.  This latter function is hidden,
even though tt(Car::setMass) has only one tt(size_t) argument. To implement
tt(Truck::setMass) we could write:
     verb(
void Truck::setMass(size_t tractor_mass, size_t trailer_mass)
{
    d_mass = tractor_mass + trailer_mass;
    Car::setMass(tractor_mass);     // note: Car:: is required
}
        )
    it() Outside of the class tt(Car::setMass) is
accessed using the i(scope resolution operator). So, if a tt(Truck truck) needs
to set its tt(Car) mass, it must use
    verb(
truck.Car::setMass(x);
        )

    it() An alternative to using the scope resolution operator is to add a
member having the same function prototype as the base class member to the
derived class's interface. This derived class member could be implemented
inline to call the base class member. E.g., we add the following member to the
tt(class Truck):
    verb(
// in the interface:
void setMass(size_t tractor_mass);

// below the interface:
inline void Truck::setMass(size_t tractor_mass)
{
    (d_mass -= Car::mass()) + tractor_mass;
    Car::setMass(tractor_mass);
}
        )
    Now the single argument tt(setMass) member function can be used by
tt(Truck) objects without using the scope resolution operator. As the
function is defined inline, no overhead of an additional function call is
involved.

    it() hi(derived class: using declaration)hi(using: in derived classes) To
prevent hiding the base class members a tt(using) declaration may be added to
the derived class interface. The relevant section of tt(Truck)'s class
interface then becomes:
    verb(
class Truck: public Car
{
    public:
        using Car::setMass;
        void setMass(size_t tractor_mass, size_t trailer_mass);
};
        )
    A using declaration imports (all overloaded versions of) the mentioned
member function directly into the derived class's interface. If a base class
member has a signature that is identical to a derived class member then
compilation fails (a tt(using Car::mass) declaration cannot be added to
tt(Truck)'s interface). Now code may use tt(truck.setMass(5000)) as well as
tt(truck.setMass(5000, 2000)).

    Using declarations obey access rights. To prevent non-class members from
using tt(setMass(5000)) without a scope resolution operator but allowing
derived class members to do so the tt(using Car::setMass) declaration
should be put in the class tt(Truck)'s private section.

    it() The function tt(mass) is also already defined in tt(Car), as
it was inherited from tt(Vehicle). In this case, the class tt(Truck) 
em(redefines) this member function to return the truck's full mass:
     verb(
size_t Truck::mass() const
{
    return d_mass;
}
        )
    )
    Example:
        verb(
    int main()
    {
        Land vehicle(1200, 145);
        Truck lorry(3000, 120, "Juggernaut", 2500);
    
        lorry.Vehicle::setMass(4000);
    
        cout << '\n' << "Tractor weighs " <<
                        lorry.Vehicle::mass() << '\n' <<
            "Truck + trailer weighs " << lorry.mass() << '\n' <<
            "Speed is " << lorry.speed() << '\n' <<
            "Name is " << lorry.name() << '\n';
    }
        )

    The class tt(Truck) was derived from tt(Car). However, one might question
this class design. Since a truck is conceived of as a combination of an
tractor and a trailer it is probably better defined using a mixed design,
using inheritance for the tractor part (inheriting from tt(Car), and
composition for the trailer part).

This redesign changes our point of view from a tt(Truck) em(being) a tt(Car)
(and some strangely added data members) to a tt(Truck) still em(being) an
tt(Car) (the tractor) and em(containing) a tt(Vehicle) (the trailer).

tt(Truck)'s interface is now very specific, not requiring users to study
tt(Car)'s and tt(Vehicle)'s interfaces and it opens up possibilities for
defining `road trains': tractors towing multiple trailers. Here is an example
of such an alternate class setup:
        verb(
    class Truck: public Car    // the tractor
    {
        Vehicle d_trailer;      // use vector<Vehicle> for road trains

        public:
            Truck();
            Truck(size_t tractor_mass, size_t speed, char const *name,
                  size_t trailer_mass);

            void setMass(size_t tractor_mass, size_t trailer_mass);
            void setTractorMass(size_t tractor_mass);
            void setTrailerMass(size_t trailer_mass);

            size_t tractorMass() const;
            size_t trailerMass() const;
        // consider:
            Vehicle const &trailer() const;
    };
        )