- •Table of Contents
- •Introduction
- •What Is C++?
- •Conventions Used in This Book
- •How This Book Is Organized
- •Part I: Introduction to C++ Programming
- •Part III: Introduction to Classes
- •Part IV: Inheritance
- •Part V: Optional Features
- •Part VI: The Part of Tens
- •Icons Used in This Book
- •Where to Go from Here
- •Grasping C++ Concepts
- •How do I program?
- •Installing Dev-C++
- •Setting the options
- •Creating Your First C++ Program
- •Entering the C++ code
- •Building your program
- •Executing Your Program
- •Dev-C++ is not Windows
- •Dev-C++ help
- •Reviewing the Annotated Program
- •Examining the framework for all C++ programs
- •Clarifying source code with comments
- •Basing programs on C++ statements
- •Writing declarations
- •Generating output
- •Calculating Expressions
- •Storing the results of expression
- •Declaring Variables
- •Declaring Different Types of Variables
- •Reviewing the limitations of integers in C++
- •Solving the truncation problem
- •Looking at the limits of floating-point numbers
- •Declaring Variable Types
- •Types of constants
- •Special characters
- •Are These Calculations Really Logical?
- •Mixed Mode Expressions
- •Performing Simple Binary Arithmetic
- •Decomposing Expressions
- •Determining the Order of Operations
- •Performing Unary Operations
- •Using Assignment Operators
- •Why Mess with Logical Operations?
- •Using the Simple Logical Operators
- •Storing logical values
- •Using logical int variables
- •Be careful performing logical operations on floating-point variables
- •Expressing Binary Numbers
- •The decimal number system
- •Other number systems
- •The binary number system
- •Performing Bitwise Logical Operations
- •The single bit operators
- •Using the bitwise operators
- •A simple test
- •Do something logical with logical calculations
- •Controlling Program Flow with the Branch Commands
- •Executing Loops in a Program
- •Looping while a condition is true
- •Using the for loop
- •Avoiding the dreaded infinite loop
- •Applying special loop controls
- •Nesting Control Commands
- •Switching to a Different Subject?
- •Writing and Using a Function
- •Divide and conquer
- •Understanding the Details of Functions
- •Understanding simple functions
- •Understanding functions with arguments
- •Overloading Function Names
- •Defining Function Prototypes
- •Variable Storage Types
- •Including Include Files
- •Considering the Need for Arrays
- •Using an array
- •Initializing an array
- •Accessing too far into an array
- •Using arrays
- •Defining and using arrays of arrays
- •Using Arrays of Characters
- •Creating an array of characters
- •Creating a string of characters
- •Manipulating Strings with Character
- •String-ing Along Variables
- •Variable Size
- •Address Operators
- •Using Pointer Variables
- •Comparing pointers and houses
- •Using different types of pointers
- •Passing Pointers to Functions
- •Passing by value
- •Passing pointer values
- •Passing by reference
- •Limiting scope
- •Examining the scope problem
- •Providing a solution using the heap
- •Defining Operations on Pointer Variables
- •Re-examining arrays in light of pointer variables
- •Applying operators to the address of an array
- •Expanding pointer operations to a string
- •Justifying pointer-based string manipulation
- •Applying operators to pointer types other than char
- •Contrasting a pointer with an array
- •Declaring and Using Arrays of Pointers
- •Utilizing arrays of character strings
- •Identifying Types of Errors
- •Choosing the WRITE Technique for the Problem
- •Catching bug #1
- •Catching bug #2
- •Calling for the Debugger
- •Defining the debugger
- •Finding commonalities among us
- •Running a test program
- •Single-stepping through a program
- •Abstracting Microwave Ovens
- •Preparing functional nachos
- •Preparing object-oriented nachos
- •Classifying Microwave Ovens
- •Why Classify?
- •Introducing the Class
- •The Format of a Class
- •Accessing the Members of a Class
- •Activating Our Objects
- •Simulating real-world objects
- •Why bother with member functions?
- •Adding a Member Function
- •Creating a member function
- •Naming class members
- •Calling a Member Function
- •Accessing a member function
- •Accessing other members from a member function
- •Defining a Member Function in the Class
- •Keeping a Member Function After Class
- •Overloading Member Functions
- •Defining Arrays of and Pointers to Simple Things
- •Declaring Arrays of Objects
- •Declaring Pointers to Objects
- •Dereferencing an object pointer
- •Pointing toward arrow pointers
- •Passing Objects to Functions
- •Calling a function with an object value
- •Calling a function with an object pointer
- •Calling a function by using the reference operator
- •Returning to the Heap
- •Comparing Pointers to References
- •Linking Up with Linked Lists
- •Performing other operations on a linked list
- •Hooking up with a LinkedListData program
- •A Ray of Hope: A List of Containers Linked to the C++ Library
- •Protecting Members
- •Why you need protected members
- •Discovering how protected members work
- •Protecting the internal state of the class
- •Using a class with a limited interface
- •Creating Objects
- •Using Constructors
- •Why you need constructors
- •Making constructors work
- •Dissecting a Destructor
- •Why you need the destructor
- •Working with destructors
- •Outfitting Constructors with Arguments
- •Justifying constructors
- •Using a constructor
- •Defaulting Default Constructors
- •Constructing Class Members
- •Constructing a complex data member
- •Constructing a constant data member
- •Constructing the Order of Construction
- •Local objects construct in order
- •Static objects construct only once
- •Global objects construct in no particular order
- •Members construct in the order in which they are declared
- •Destructors destruct in the reverse order of the constructors
- •Copying an Object
- •Why you need the copy constructor
- •Using the copy constructor
- •The Automatic Copy Constructor
- •Creating Shallow Copies versus Deep Copies
- •Avoiding temporaries, permanently
- •Defining a Static Member
- •Why you need static members
- •Using static members
- •Referencing static data members
- •Uses for static data members
- •Declaring Static Member Functions
- •What Is This About, Anyway?
- •Do I Need My Inheritance?
- •How Does a Class Inherit?
- •Using a subclass
- •Constructing a subclass
- •Destructing a subclass
- •Having a HAS_A Relationship
- •Why You Need Polymorphism
- •How Polymorphism Works
- •When Is a Virtual Function Not?
- •Considering Virtual Considerations
- •Factoring
- •Implementing Abstract Classes
- •Describing the abstract class concept
- •Making an honest class out of an abstract class
- •Passing abstract classes
- •Factoring C++ Source Code
- •Defining a namespace
- •Implementing Student
- •Implementing an application
- •Project file
- •Creating a project file under Dev-C++
- •Comparing Operators with Functions
- •Inserting a New Operator
- •Overloading the Assignment Operator
- •Protecting the Escape Hatch
- •How Stream I/O Works
- •The fstream Subclasses
- •Reading Directly from a Stream
- •Using the strstream Subclasses
- •Manipulating Manipulators
- •Justifying a New Error Mechanism?
- •Examining the Exception Mechanism
- •What Kinds of Things Can I Throw?
- •Adding Virtual Inheritance
- •Voicing a Contrary Opinion
- •Generalizing a Function into a Template
- •Template Classes
- •Do I Really Need Template Classes?
- •Tips for Using Templates
- •The string Container
- •The list Containers
- •Iterators
- •Using Maps
- •Enabling All Warnings and Error Messages
- •Insisting on Clean Compiles
- •Limiting the Visibility
- •Avoid Overloading Operators
- •Heap Handling
- •Using Exceptions to Handle Errors
- •Avoiding Multiple Inheritance
- •Customize Editor Settings to Your Taste
- •Highlight Matching Braces/Parentheses
- •Enable Exception Handling
- •Include Debugging Information (Sometimes)
- •Create a Project File
- •Customize the Help Menu
- •Reset Breakpoints after Editing the File
- •Avoid Illegal Filenames
- •Include #include Files in Your Project
- •Executing the Profiler
- •System Requirements
- •Using the CD with Microsoft Windows
- •Using the CD with Linux
- •Development tools
- •Program source code
- •Index
Chapter 20: Inheriting a Class 263
Another minor side effect has to do with software modification. Suppose you inherit from some existing class. Later, you find that the base class doesn’t do exactly what the subclass needs. Or, perhaps, the class has a bug. Modifying the base class might break any code that uses that base class. Creating and using a new subclass that overloads the incorrect feature solves your prob lem without causing someone else further problems.
How Does a Class Inherit?
Here’s the GraduateStudent example filled out into a program
InheritanceExample:
//
//InheritanceExample - demonstrate an inheritance
// |
relationship in which the subclass |
// |
constructor passes argument information |
// |
to the constructor in the base class |
// |
|
#include <cstdio> |
|
#include <cstdlib> |
|
#include <iostream> |
|
#include <strings.h> |
|
using namespace std; |
|
// Advisor - empty class |
|
class Advisor {}; |
|
const int MAXNAMESIZE = 40; |
|
class Student |
|
{ |
public: |
|
|
|
Student(char *pName = “no name”) |
|
: average(0.0), semesterHours(0) |
|
{ |
|
strncpy(name, pName, MAXNAMESIZE); |
|
name[MAXNAMESIZE - 1] = ‘\0’; |
|
cout << “constructing student “ |
|
<< name |
|
<< endl; |
|
} |
|
void addCourse(int hours, float grade) |
|
{ |
|
cout << “adding grade to “ << name << endl; |
|
average = (semesterHours * average + grade); |
|
semesterHours += hours; |
|
average = average / semesterHours; |
|
} |
264 Part IV: Inheritance
int hours( ) { return semesterHours;} float gpa( ) { return average;}
protected:
char name[MAXNAMESIZE]; int semesterHours; float average;
};
class GraduateStudent : public Student
{
public:
GraduateStudent(char *pName, Advisor& adv, float qG = 0.0)
: Student(pName), advisor(adv), qualifierGrade(qG)
{
cout << “constructing graduate student “
<<pName
<<endl;
}
float qualifier( ) { return qualifierGrade; }
protected: Advisor advisor;
float qualifierGrade;
};
int main(int nNumberofArgs, char* pszArgs[])
{
Advisor advisor;
//create two Student types Student llu(“Cy N Sense”);
GraduateStudent gs(“Matt Madox”, advisor, 1.5);
//now add a grade to their grade point average llu.addCourse(3, 2.5);
gs.addCourse(3, 3.0);
//display the graduate student’s qualifier grade cout << “Matt’s qualifier grade = “
<<gs.qualifier()
<<endl;
//wait until user is ready before terminating program
//to allow the user to see the program results system(“PAUSE”);
return 0;
}
This program demonstrates the creation and use of two objects, one of class Student and a second of GraduateStudent. The output of this program is as follows:
Chapter 20: Inheriting a Class 265
constructing student Cy N Sense constructing student Matt Madox constructing graduate student Matt Madox adding grade to Cy N Sense
adding grade to Matt Madox Matt’s qualifier grade = 1.5 Press any key to continue . . .
Using a subclass
The class Student has been defined in the conventional fashion. The class GraduateStudent is a bit different, however; the colon followed by the phrase public Student at the beginning of the class definition declares
GraduateStudent to be a subclass of Student.
The appearance of the keyword public implies that there is probably pro tected inheritance as well. All right, it’s true, but protected inheritance is beyond the scope of this book.
Programmers love inventing new terms or giving new meaning to existing terms. Heck, programmers even invent new terms and then give them a second meaning. Here is a set of equivalent expressions that describes the same relationship:
GraduateStudent is a subclass of Student.
Student is the base class or is the parent class of GraduateStudent.
GraduateStudent inherits from Student.
GraduateStudent extends Student.
As a subclass of Student, GraduateStudent inherits all of its members. For example, a GraduateStudent has a name even though that member is declared up in the base class. However, a subclass can add its own members, for example qualifierGrade. After all, gs quite literally IS_A Student plus a little bit more than a Student.
The main() function declares two objects, llu of type Student and gs of type GraduateStudent. It then proceeds to access the addCourse() member function for both types of students. main() then accesses the qualifier() function that is only a member of the subclass.
Constructing a subclass
Even though a subclass has access to the protected members of the base class and could initialize them, each subclass is responsible for initializing itself.
266 Part IV: Inheritance
Before control passes beyond the open brace of the constructor for GraduateStudent, control passes to the proper constructor of Student. If Student were based on another class, such as Person, the constructor for that class would be invoked before the Student constructor got control. Like a skyscraper, the object is constructed starting at the “base”-ment class and working its way up the class structure one story at a time.
Just as with member objects, you often need to be able to pass arguments to the base class constructor. The example program declares the subclass con structor as follows:
GraduateStudent(char *pName, Advisor& adv, float qG = 0.0) : Student(pName), advisor(adv), qualifierGrade(qG)
{
// whatever construction code goes here
}
Here the constructor for GraduateStudent invokes the Student construc tor, passing it the argument pName. C++ then initializes the members advisor and qualifierGrade before executing the statements within the constructor’s open and close braces.
The default constructor for the base class is executed if the subclass makes no explicit reference to a different constructor. Thus, in the following code snippet the Pig base class is constructed before any members of LittlePig, even though LittlePig makes no explicit reference to that constructor:
class Pig
{
public:
Pig() : pHouse(null) {}
protected: House* pHouse;
};
class LittlePig : public Pig
{
public:
LittlePig(float volStraw, int numSticks, int numBricks)
: straw(volStraw), sticks(numSticks), bricks(numBricks)
{}
protected: float straw; int sticks; int bricks;
};
Similarly, the copy constructor for a base class is invoked automatically.