|
| Thursday, October 12, 2006 |
| Inheritance - Polymorphism |
Example so far:
// Vehicle class
class Vehicle
{
private:
float distance; // in miles
float speed; // in miles per hourpublic:
// Constructor
Vehicle() { distance = 0.0; speed = 0.0; }// Rate = distance/speed
float computeDuration();// Access methods
float getSpeed() { return speed; }
void setSpeed(float s) { speed = s; }
float getDistance() { return distance; }
void setDistance(float d) { distance = d; }
};float Vehicle::computeDuration()
{
return ( distance / speed );
}
// Wheeled vehicle (derived from Vehicle class)
class WheelVehicle : public Vehicle
{
private:
int wheels; // number of wheelspublic:
// Constructor
WheelVehicle() { wheels = 0; }// Access methods
void setWheels(int w) { wheels = w; }
int getWheels() { return wheels; }
};
// Truck class (derived from WheelVehicle class)
class Truck : public WheelVehicle
{
private:
float carryingLoad;public:
Truck() { carryingLoad = 0.0; }void setLoad( float l ) { carryingLoad = l; }
float getLoad() { return carryingLoad; }
};
// Application
#include <stdio.h>main()
{
WheelVehicle wheely;
wheely.setWheels(4);
wheely.setSpeed(65.0);
wheely.setDistance(300.0);
printf( "Duration for wheely: %f hrs\n", wheelv.computeDuration() );
Truck semi;
semi.setLoad(2);
semi.setWheels(18);
semi.setSpeed(55.0);
semi.setDistance(300.0);
printf( "Duration for truck: %f hrs\n", semi.computeDuration() );
}
Output
Duration for wheely: 4.615385 hours
Duration for truck: 5.454545 hours
Base and Derived relationships
- Derived class can be assigned to any of its public base classes without explicit casting
- Sets up a "generic" style of programming where the actual type of a class is unknown
Example 1
#include <stdio.h>
main()
{
Truck semi;
semi.setLoad(2);
semi.setWheels(18);
semi.setSpeed(55.0);
semi.setDistance(300.0);
printf( "Duration for truck: %f hrs\n", semi.computeDuration() );
// Derived class assigned to base class
WheelVehicle &wv = semi;
printf( "Duration for wv: %f hrs\n", wv.computeDuration() );
// Derived class assigned to parent of base class
Vehicle &v = semi;
printf( "Duration for v: %f hrs\n", v.computeDuration() );
// Derived class address assigned to base class pointer
Vehicle *vp = ;
printf( "Duration for vp: %f hrs\n", vp->computeDuration() );
}
Output
Duration for truck: 5.454545 hrs
Duration for wv: 5.454545 hrs
Duration for v: 5.454545 hrs
Duration for vp: 5.454545 hrs
Example 2
#include <stdio.h>
void printDuration( Vehicle *v );
main()
{
Truck semi;
semi.setLoad(2);
semi.setWheels(18);
semi.setSpeed(55.0);
semi.setDistance(300.0);
printf( "Duration for truck: %f hrs\n", semi.computeDuration() );// Can pass address of derived type
printDuration( &semi );// Can pass address of derived type
Vehicle *vp = ;
printDuration( vp );
}void printDuration( Vehicle *v )
{
printf( "Duration: %f hrs\n",
v->computeDuration() );
}
Output
Duration for truck: 5.454545 hrs
Duration: 5.454545 hrs
Duration: 5.454545 hrs
- Which computeDuration method (in which class) is being called above?
- We might want to have different meanings for computeDuration in derived classes
- If we could, how will our printDuration function know the difference?
- How can we distinguish between the different derived class types?
- A first attempt (in a nonobject-oriented manner) might look something like this...
Pseudocode example
#define VEHICLE 0
#define WHEELV 1
#define TRUCK 2add
int whatIsMyType() { return (VEHICLE, WHEELV, TRUCK) }
to every classvoid printDuration( Vehicle *v )
{
switch( v->whatIsMyType() )
{
case VEHICLE:
....
break;case WHEELV:
....
break;case TRUCK:
....
break;
}
}
- Note that the burden of "type resolution" rests with the programmer
- Would be nice if we could shift this burden to the compiler....
Dynamic (late) binding
- Class type determined at run-time by compiler
- Compiler cannot determine class type at compile time
- Used with pointers to objects onlyVehicle *v = new Truck;
- Compiler doesn't know which object type is being referenced
- Which class is it? Vehicle? WheelVehicle? Truck?
- No need for programmer to determine type in example above
- Reduces size and complexity of code
- Makes code more extensible (further derived classes follow suit)
- Opposite of static (early) binding (compiler knows at compile time)
- What type of binding is used with overloaded functions?
- To implement dynamic binding, we need a new mechanism...
Virtual Functions
- Mechanism for modifying inherited functions
- Different methods for different derived classes
- Derived class overrides base class function and provides its own version
- A better term would be "adaptable" rather than virtual
- Overridden method in derived class is adapted to fit new situation
- Functions are declared to be virtual under the following conditions
1) A class expects to be an object of derivation
2) The implementation of the function is type-dependent- Objects respond to same commands in different ways
- This feature is also called polymorphism
- Where else have seen an example in C++ of polymorphism?
Virtual Function syntax
- Precede function declaration with keyword virtual
- Keyword need only be denoted in base class, not necessary in derived class(es)virtual <return type> <name> (<arg1, arg2, ...>)
Rules when declaring virtual functions properly:
1) The virtual keyword must be used in the base class
2) Both base and derived class methods must have same parameters and types
(Their function declaration must be equivalent)
Example
- Consider a general base class describing an error message
- Create a more specific type of error message with derived class
// General error message
class error_msg
{
public:
virtual void display()
{
printf( "An error has occurred\n" );
}
};
// Specific type of syntax error message
class syntax_msg : public error_msg
{
public:
void display()
{
printf( "Semicolon missing in statement\n" );
}
};
#include <stdio.h>
main()
{
error_msg *m[2]; // array of object pointers
error_msg msg; // error message
syntax_msg smsg; // syntax error messagem[0] = &msg;
m[1] = &smsg;m[0]->display();
m[1]->display();
}
Output
An error has occurred
Semicolon missing in statementVirtual functions and overloaded functions
- Note how at run-time, the program determined the class type of the pointers
- This feature is known as dynamic binding (type determined at run-time)
- Why bother with virtual functions, why not just override the function?
- Let's see why....class error_msg
{
public:// No longer virtual!
void display()
{
printf( "An error has occurred\n" );
}
};class syntax_msg : public error_msg
{
public:// Overrides function in base class
void display()
{
printf( "Semicolon missing in statement\n" );
}
};#include <stdio.h>
main()
{
error_msg *m[2];
error_msg msg;
syntax_msg smsg;m[0] = &msg;
m[1] = &smsg;m[0]->display();
m[1]->display();
}Output
An error has occurred
An error has occurred- When using base class pointers to derived objects, if the virtual keyword is not used...
1) and the base and derived methods are similar in name and parameters
the base method overrides the derived method2) and the base and derived methods are similar in name, different in parameters
the derived method overloads the base method
Virtual functions and pointers
- Consider the following change to our example#include <stdio.h>
main()
{
error_msg m[2];
error_msg msg;
syntax_msg smsg;m[0] = msg;
m[1] = smsg;m[0].display();
m[1].display();
}
Output
An error has occurred
An error has occurred
- What happened?
- What have we changed?
- Remember dynamic binding works with pointers!
Final Example
- Let's return to our original example using vehicle types
// General vehicle class
class Vehicle
{
private:
float distance; // in miles
float speed; // in miles per hours
char name[100]; // added name of vehiclepublic:
Vehicle()
{
distance = 0.0;
speed = 0.0;
}// Virtual function (type dependent)
virtual float computeDuration();
// Access methods
void setName(char *s) { strcpy(name,s); }
char *getName() { return name; }
float getSpeed() { return speed; }
void setSpeed(float s) { speed = s; }
float getDistance() { return distance; }
void setDistance(float d) { distance = d; }
};float Vehicle::computeDuration()
{
return ( distance / speed );
}
// Specific type of vehicle w/ wheels
class WheelVehicle : public Vehicle
{
private:
int wheels;public:
WheelVehicle() { wheels = 0; }// Derived virtual method.
// Even if you don't include the
// keyword virtual, which is optional,
// the function is still virtual
// because it is virtual in the base
// class.
virtual float computeDuration();// Access methods
void setWheels(int w) { wheels = w; }
int getWheels() { return wheels; }
};float WheelVehicle::computeDuration();
{
float tmp = getDistance()/getSpeed();
float road_construction = 0.1;return (tmp + tmp*road_construction);
}
// Specific wheeled vehicle type
class Truck : public WheelVehicle
{
private:
float carryingLoad; // in tonspublic:
Truck() { carryingLoad = 0.0; }// Derived virtual function
float computeDuration();void setLoad( float l ) { carryingLoad = l; }
float getLoad() { return carryingLoad; }
};float Truck::computeDuration();
{
float tmp = getDistance()/getSpeed();
float road_construction = 0.1;return (tmp + tmp*road_construction + tmp*carryingLoad*0.1);
}
#include <stdio.h>
#include <string.h>main()
{
// Instantiate a car
WheelVehicle car;
car.setName("car");
car.setWheels(4);
car.setSpeed(65.0);// Instantiate a semi truck
Truck semi;
semi.setName("semi");
semi.setLoad(2);
semi.setWheels(18);
semi.setSpeed(55.0);// Instantiate a pickup truck
Truck pickup;
pickup.setName("pickup");
pickup.setLoad(0.2);
pickup.setWheels(4);
pickup.setSpeed(70.0);// Create a fleet for a company
Vehicle *fleet[3];
fleet[0] = &car;
fleet[1] = ;
fleet[2] = &pickup;
// Compute duration of trip for different vehicle types
for( int i = 0; i < 3; ++i )
{
// Set a distance
fleet[i]->setDistance(300.0);// Print the duration of the trip
printf("Fleet %d - %s, Duration: %.2f\n", i,
fleet[i]->getName(), fleet[i]->computeDuration() );
}
}Output
Fleet 0 - car, Duration: 4.15
Fleet 1 - semi, Duration: 3.82
Fleet 2 - pickup, Duration: 3.77
- Note how different methods are called for different types
- Note how dynamic binding occurs at run-time
- Note how this is implemented with virtual functions and object pointers
Readings
Deitel & Deitel Fourth Edition: 9.4 - 9.7, 10.1 - 10.7
| Back to Csc 125 - Computer Science II, Programming in C++ |
| Scott Badman Office: B132 Phone: 353-2250 sbadman@parkland.edu |
Parkland College, 2400 W. Bradley Avenue, Champaign, IL 61821 |