/*
Consistency is fundamental in class design and implementation.
We can speak about external consistency (in the interface) and
internal consistency (in the implementation of each objects state.
When inheritance is used, there is a further issue of consistency
to consider: consistency in the interface between a base class and
its derived classes. In addition to the public interface, a base
class may have a protected interface consisting of the protected
members accessible from derived classes. By declaring protected
members, a base class provides special access for use by its derived
classes. The derived classes must use this access consistently with
the implementation of the base class.
Car and Truck make inconsistent use of the base protected member
plate. The default constructor of Vehicle set plate to 0 pointer.
However identify() function in Car and Truck pass plate to printf
vithout checking for a null value. If the parameter for %s is a
null value, the behaviour of printf is not defined.
The following code has an undefined result:
Truck t;
t.identify();
*/
#include <stdio.h>
#include <string.h>
class Vehicle
{
public:
Vehicle() { plate = 0; }
Vehicle(char *p)
{
plate = new char[strlen(p)+1];
strcpy(plate, p);
}
~Vehicle() { delete [] plate; }
virtual void identify() { printf("generic vehicle\n"); }
protected:
char *plate;
};
/*
The root of the problem is in the identify() member function
in the derived classes. They behave inconsistently with respect
to the protected interface of their base class. The base class
represents a missing license plate number with a null pointer,
but the derived identify() functions assume that the pointer is
always non-null.
A derived class cannot be coded before its base class. A derived
class should conform consistently to the conventions established
by its base class. In our example, the derived classes do not
account for all of the legitimate states of the base part of their
objects.
To correct the bug, Car::identify() and Truck::identify() should
test for null pointer and take a reasonable action to handle that
case.
*/
class Car : public Vehicle
{
public:
Car() : Vehicle() { }
Car(char *p) : Vehicle(p) { };
void identify()
{
char *p = plate ? plate : "<node>";
printf("car with plate %s\n", p);
}
};
class Truck : public Vehicle
{
public:
Truck() : Vehicle() { }
Truck(char *p) : Vehicle(p) { };
void identify()
{
char *p = plate ? plate : "<node>";
printf("truck with plate %s\n", p);
}
};
/*
An other important point to see is, that Vehicle has a non-virtual
destructor. We know, that for a polymorphic type a virtual destructor
is very important. However, if the derived classes have no destructor
defined, the non-virtual base destructor causes no problem.As long as
the derived classes do not need destructors, the base class may remain
as it is.
(This rule does not adequately address multiply inheritance.)
*/
class Garage
{
public:
Garage(int max);
~Garage();
int accept(Vehicle*);
Vehicle *release(int bay);
void listVehicles();
private:
int maxVehicles;
Vehicle **parked;
};
Garage::Garage(int max)
{
maxVehicles = max;
parked = new Vehicle*[maxVehicles];
for( int bay = 0; bay < maxVehicles; ++bay )
{
parked[bay] = 0;
}
}
Garage::~Garage()
{
delete [] parked;
}
int Garage::accept(Vehicle *veh)
{
for( int bay = 0; bay < maxVehicles; ++bay )
if( !parked[bay] )
{
parked[bay] = veh;
return( bay );
}
return( -1 ); // No free bay
}
Vehicle *Garage::release(int bay)
{
if( bay < 0 || bay > maxVehicles )
return 0;
Vehicle *veh = parked[bay];
parked[bay] = 0;
return( veh );
}
void Garage::listVehicles()
{
for( int bay = 0; bay < maxVehicles; ++bay )
if( parked[bay] )
{
printf("Vehicle in bay %d is: ", bay);
parked[bay]->identify();
}
}
Car c1("AAA100");
Car c2("BBB200");
Car c3("CCC300");
Truck t1("TTT999");
Truck t2("SSS888");
Truck t3("UUU777");
int main( )
{
Garage Park(14);
Park.accept(&c1);
int t2bay = Park.accept(&t2);
Park.accept(&c3);
Park.accept(&t2);
Park.release(t2bay);
Park.listVehicles();
return 0;
}
/*
The output of the program is the following:
Vehicle in bay 0 is: car with plate AAA100
Vehicle in bay 2 is: car with plate CCC300
Vehicle in bay 3 is: truck with plate SSS888
*/