Off a base-class pointer, the compiler allows us to invoke only base-class member functions. Thus, if a base-class pointer is aimed at a derived-class object, and an attempt is made to access a derived-class-only member function, a compilation error will occur.
Figure 12.3 shows the consequences of attempting to invoke a derived-class member function off a base-class pointer. [Note: We’re again reusing the versions of classes CommissionEmployee
and BasePlusCommissionEmployee
from Section 11.3.5.] Line 11 creates commissionEmployeePtr
—a pointer to a CommissionEmployee
object—and lines 12–13 create a BasePlusCommissionEmployee
object. Line 16 aims the base-class commissionEmployeePtr
at derived-class object basePlusCommissionEmployee
. Recall from Section 12.3.1 that this is allowed, because a BasePlusCommissionEmployee
is a CommissionEmployee
(in the sense that a BasePlusCommissionEmployee
object contains all the functionality of a CommissionEmployee
object). Lines 20–24 invoke base-class member functions getFirstName
, getLastName
, getSocialSecurityNumber
, getGrossSales
and getCommissionRate
off the base-class pointer. All of these calls are allowed, because BasePlusCommissionEmployee
inherits these member functions from CommissionEmployee
. We know that commissionEmployeePtr
is aimed at a BasePlusCommissionEmployee
object, so in lines 28–29 we attempt to invoke BasePlusCommissionEmployee
member functions getBaseSalary
and setBaseSalary
. The compiler generates errors on both of these calls, because they’re not made to member functions of base-class CommissionEmployee
. The handle can be used to invoke only those functions that are members of that handle’s associated class type. (In this case, off a CommissionEmployee *
, we can invoke only CommissionEmployee
member functions setFirstName
, getFirstName
, setLastName
, getLastName
, setSocialSecurityNumber
, getSocialSecurityNumber
, setGrossSales
, getGrossSales
, setCommissionRate
, getCommissionRate
, earnings
and print
.)
1 // Fig. 12.3: fig12_03.cpp
2 // Attempting to invoke derived-class-only member functions
3 // via a base-class pointer.
4 #include <string>
5 #include "CommissionEmployee.h"
6 #include "BasePlusCommissionEmployee.h"
7 using namespace std;
8
9 int main()
10 {
11 CommissionEmployee *commissionEmployeePtr = nullptr; // base class ptr
12 BasePlusCommissionEmployee basePlusCommissionEmployee(
13 "Bob", "Lewis", "333-33-3333", 5000, .04, 300 ); // derived class
14
15 // aim base-class pointer at derived-class object (allowed)
16 commissionEmployeePtr = &basePlusCommissionEmployee;
17
18 // invoke base-class member functions on derived-class
19 // object through base-class pointer (allowed)
20 string firstName = commissionEmployeePtr->getFirstName();
21 string lastName = commissionEmployeePtr->getLastName();
22 string ssn = commissionEmployeePtr->getSocialSecurityNumber();
23 double grossSales = commissionEmployeePtr->getGrossSales();
24 double commissionRate = commissionEmployeePtr->getCommissionRate();
25
26 // attempt to invoke derived-class-only member functions
27 // on derived-class object through base-class pointer (disallowed)
28 double baseSalary = commissionEmployeePtr->getBaseSalary();
29 commissionEmployeePtr->setBaseSalary( 500 );
30 } // end main
GNU C++ compiler error messages:
fig12_03.cpp:28:47: error: 'class CommissionEmployee' has no member named
'getBaseSalary'
fig12_03.cpp:29:27: error: 'class CommissionEmployee' has no member named
'setBaseSalary'
The compiler will allow access to derived-class-only members from a base-class pointer that’s aimed at a derived-class object if we explicitly cast the base-class pointer to a derived-class pointer—this is known as downcasting. As you know, it’s possible to aim a base-class pointer at a derived-class object. However, as we demonstrated in Fig. 12.3, a base-class pointer can be used to invoke only the functions declared in the base class. Downcasting allows a derived-class-specific operation on a derived-class object pointed to by a base-class pointer. After a downcast, the program can invoke derived-class functions that are not in the base class. Downcasting is a potentially dangerous operation. Section 12.8 demonstrates how to safely use downcasting.
Software Engineering Observation 12.3
If the address of a derived-class object has been assigned to a pointer of one of its direct or indirect base classes, it’s acceptable to cast that base-class pointer back to a pointer of the derived-class type. In fact, this must be done to call derived-class member functions that do not appear in the base class.