mirror of
https://gitlab.com/fbb-git/cppannotations
synced 2024-09-29 17:42:40 +02:00
175 lines
6.4 KiB
Text
175 lines
6.4 KiB
Text
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;
|
|
};
|
|
)
|
|
|
|
|
|
|
|
|
|
|