Conversions between Base and Derived Classes

Since a salesperson is a kind of wage-earning employee, it makes sense to be able to use a SalesPerson object whenever an WageEmployee object is needed. To support this relationship, C++ lets you implicitly convert an instance of a derived class into an instance of a base class. For example:

WageEmployee aWorker;

SalesPerson aSeller( "John Smith" );

aWorker = aSeller; // Convert SalesPerson to WageEmployee

// derived => base

All the members of the SalesPerson object receive the values of the corresponding members in the WageEmployee object. However, the reverse assignment is not legal:

aSeller = aWorker; // Error; cannot convert

Since SalesPerson has members that WageEmployee doesn't, their values would be undefined after such an assignment. This restriction follows the conceptual relationship between the types of employee: a worker is not necessarily a salesperson.

You can also implicitly convert a pointer to a derived class object into a pointer to a base class object. For example:

Employee *empPtr;

WageEmployee aWorker( "Bill Shapiro" );

SalesPerson aSeller( "John Smith" );

Manager aBoss( "Mary Brown" );

empPtr = &aWorker; // Convert WageEmployee * to Employee *

empPtr = &aSeller; // Convert SalesPerson * to Employee *

empPtr = &aBoss; // Convert Manager * to Employee *

You can use a pointer to an Employee to point to a WageEmployee object, a SalesPerson object, or a Manager object.

When you refer to an object through a pointer, the type of the pointer determines which member functions you can call. If you refer to a derived class object with a base class pointer, you can call only the functions defined by the base class. For example:

SalesPerson aSeller( "John Smith" );

SalesPerson *salePtr;

WageEmployee *wagePtr;

salePtr = &aSeller;

wagePtr = &aSeller;

wagePtr->setHours( 40.0 ); // Call WageEmployee::setHours

salePtr->setWage( 6.0 ); // Call WageEmployee::setWage

wagePtr->setSales( 1000.0 ); // Error;

// no WageEmployee::setSales

salePtr->setSales( 1000.0 ); // Call SalesPerson::setSales

salePtr->setCommission( 0.05 ); // Call SalesPerson::setCommission

Both wagePtr and salePtr point to a single SalesPerson object. You cannot call setSales through wagePtr, because WageEmployee doesn't define that member function. You have to use salePtr to call the member functions that SalesPerson defines.

If you call a member function that is defined by both the base class and the derived class, the function that is called depends on the type of the pointer. For example:

float base, total;

base = wagePtr->computePay(); // Call WageEmployee::computePay

total = salePtr->computePay(); // Call SalesPerson::computePay

When you use wagePtr, you call the version defined by WageEmployee. When you use salePtr, you call the version defined by SalesPerson.

To perform the reverse conversion (that is, from a pointer to a base class to a pointer to a derived class), you must use an explicit cast.

WageEmployee *wagePtr = &aSeller;

SalesPerson *salePtr;

salePtr = (SalesPerson *)wagePtr; // Explicit cast required

// base => derived

This conversion is dangerous, because you can't be sure what type of object the base class pointer points to. Suppose empPtr points to something other than a SalesPerson object:

Employee *empPtr = &aWorker;

SalesPerson *salePtr;

salePtr = (SalesPerson *)empPtr; // Legal, but incorrect

salePtr->setCommission( 0.05 ); // Error: aWorker has no

// setCommission member

This can cause your program to crash. Accordingly, you should be extremely careful when converting a base class pointer to a derived class pointer.