3.4 DEFINING A SUBCLASS IN C


3.4 DEFINING A SUBCLASS IN C++

As was mentioned before, being able to extend a class by defining its subclasses is one of the central features of object oriented programming. Here is the User class again and a subclass called StudentUser:

 
class User { string name; int age; public: User(string nm, int a) {name=nm; age=a;} void print() { cout << "Name: " << name << " Age: " << age; } }; class StudentUser : public User { //(A) string schoolEnrolled; public: StudentUser(string nam, int y, string school) : User(nam, y){ //(B) schoolEnrolled = school; } void print() { //(C) User::print(); //(D) cout << "School Enrolled: " << schoolEnrolled << endl; } };

In accordance with the terminology already presented, User is a base class or a superclass and StudentUser a subclass, a derived class, or an extended class. The reader should note the following special features of the definitions given above:

  • Note the class header for the definition of the StudentUser class in line (A). The colon followed by the keyword public followed by the class name User means that StudentUser is a subclass of the class User, or, equivalently, User is a superclass of StudentUser. The subclass will inherit all the members of the superclass but may or may not have direct access to them. In our example, since name and age are in the private section of User, they will be inherited by StudentUser but will not be directly visible to any functions defined for StudentUser. Issues related to what parts of a superclass are visible in a subclass will be taken up in Section 3.11.

  • In line (B), the constructor for the StudentUser subclass was defined as

          StudentUser( string nam, int y, string school ) : User(nam, y) {          schoolEnrolled = school;      } 

    The header of the constructor contains a colon followed by an invocation of the constructor of the parent class User. Thus the constructor for the subclass invokes the constructor of the superclass for assigning values to those data members that are only accessible to functions defined for the superclass. As explained more fully in Chapter 15, a derived class constructor must invoke a base class constructor before it does anything else. If a base-class constructor is not called explicitly in a derived class constructor, the system tries to invoke what's known as the no-arg constructor for the base class. No-arg constructors are discussed in Chapters 9, 11, and 15.

  • Next we turn our attention to how we defined the print() function for the subclass StudentUser in line (C)

      void print() {           User::print();           cout << "schoolEnrolled: " << schoolEnrolled << endl;      } 

Note how the subclass print() invokes in line (D) the print() defined for the parent class User to print out those member values that are not directly accessible in the subclass. Suppose we create an instance of StudentUser by

      StudentUser* p = new StudentUser( "Zaphlet", 10, "cosmology" ); 

and then if we invoke the print() function for uptr by

      p->print(); 

the following would be printed out on a standard terminal:

      Zaphlet 10 cosmology 

the first two items here are the output of the print() function defined for the superclass and the last one directly by the rest of the print() function for the subclass.

Since the print() function defined for the base class User is inherited by the derived class StudentUser and since we have provided StudentUser with its own print() method, does there exist an ambiguity between the two print() definitions for a StudentUser object. No, not at all. As far as a StudentUser object is concerned, it is the StudentUser definition of print() that holds. The general rule here, as explained further in Chapter 15, is that a function of a given name in a derived class hides all functions of the same name in the base class. Base class functions that are hidden in a derived class through this name hiding mechanism can still be accessed in the derived class through the ‘::' operator, as in line (D) of the program above.

3.4.1 A Small Demonstration of Polymorphism in C++

Now that we have a small class hierarchy, consisting of the User base class and the StudentUser derived class, we can give a small demonstration of polymorphism. This demonstration will consist of constructing an array of User objects, some of which are actually StudentUser objects. (Recall from the introduction to this chapter, a StudentUser IsA User.) We will then invoke print() on all the elements of this array, with the expectation that polymorphism will cause object-specific definitions of print() to be invoked on each of the elements.

For a C++ program to take advantage of polymorphism, the following two conditions must hold true:

  • The objects must be manipulated through pointers or references.[5]

  • The functions that are expected to behave polymorphically must be declared virtual[6] in the base class. An inherited virtual function remains virtual.

These two conditions are satisfied by the program shown below. Line (E) declares print() to be virtual in the base class and line (F) provides an override definition for print() in the derived class. In lines (G) through (J) of main, we first declare an array of User* elements and then fill this array with pointers to a mixture of objects, two of type User and one of type StudentUser. Since print() is virtual, object-specific definition of print() for each element of the array is automatically invoked at run time. As a result, the output of the program is

      Name: Buster Dandy    Age: 34      Name: Missy Showoff    Age: 25    School Enrolled: Math 
      Name: Mister Meister Age: 28 

Here is the source code for the demonstration:

 
//Polymorph.cc #include <iostream> #include <string> using namespace std; class User { string name; int age; public: User(string nm, int a) {name=nm; age=a;} virtual void print() { //(E) cout << "Name: " << name << " Age: " << age; } }; class StudentUser : public User { string schoolEnrolled; public: StudentUser(string nam, int y, string school) : User(nam, y){ schoolEnrolled = school; } void print() { //(F) User::print(); cout << " School Enrolled: " << schoolEnrolled; } }; int main() { User* users[3]; //(G) users[0] = new User( "Buster Dandy", 34 ); //(H) users[1] = new StudentUser("Missy Showoff", 25, "Math"); //(I) users[2] = new User( "Mister Meister", 28 ); //(J) for (int i=0; i<3; i++) { //(K) users[i]->print(); //(L) cout << endl; } // this program has a memory leak; ignore it for now //(M) return 0; }

Since we did not deallocate the memory that was acquired with the new operator in lines (H), (I), and (J), the program has a memory leak, as indicated by the comment line (M). We could eliminate the leak by invoking delete directly on the pointers users [0] and users [1] and on the pointer users [1] after it is cast down to be of type StudentUser*. However, a more efficient way to deallocate memory in this example would be to define a virtual destructor for the base class User even if it has a do-nothing implementation. Now the memory occupied by the array users can be freed up with a single invocation of the delete [] operator, as in

      delete[] users; 

Virtual destructors are discussed in Chapter 15. Chapter 8 presents further discussion on the delete and the delete [] operators for memory deallocation.

Our demonstration of polymorphism defined a virtual print() function in the base class and then provided an override definition for the function in the derived class. In general, there are restrictions on the syntax of an overriding function in a derived class vis-à-vis the syntax of the overridden function in a base class. These restrictions are presented in Chapter 15.

[5]The concept of a reference in C++ is discussed in Chapter 8.

[6]Virtual functions in C++ and associated concepts like virtual tables are discussed in detail in Chapter 15.




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net