There is actually no magic happening here. You have inherited all your classes from "Student", but in this case it does not change anything, because each class has (not-dynamic) methods. You would have done just as well to write each class without any inheritance, duplicating the information in the child class that was in the parent class.
The following is essentially the same as you have above, except that I had to repeat some information:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
class Student
{
protected:
long social;
char name[SIZE];
char *NamePtr;
public:
...
};
class GradStudent:
{
protected:
long social;
char name[SIZE];
char *NamePtr;
private:
char status;
public:
...
};
|
In your code, the computer can tell the difference between which class is which because
there is never any possibility of confusing them.
148 149 150
|
if (code == 1)
{
UnderGradStudent a;
|
170 171 172
|
cout << "\nName: " << a.getName() << " SSN: " << a.getSSN() << " GPA: "
<< setiosflags(ios::fixed | ios::showpoint) << setprecision(2) << a.calcGPA() << endl;
}
|
That is, the variable "a", which only exists between lines 149 and 172, is a known "UnderGradStudent".
Keep in mind that this is a different variable than the "a" that exists between lines 176 and 195, which is a known "GradStudent".
There is never any chance of confusing them.
What polymorphism allows you to do is write variables that
could be confused, because they might be different types. Back to the "Student"s: Using
virtual (or "dynamic") methods, we can make "a" a
pointer to a "Student". Since an "UnderGradStudent" and a "GradStudent" are
also "Student"s, the new variable "a" could point to any one of those.
In order to help the compiler tell the difference, you must let it know that certain methods are bound to the
actual type of Student and not just a "Student".
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
class Student
{
protected:
long social;
char name[SIZE];
char *NamePtr;
public:
Student (long = 999999999 , char [] = "unassigned");
void setSSN(long);
long getSSN();
void setName(char[]);
char* getName();
// We tell the compiler that the following function is special:
// Even though the compiler is looking at a Student*, the actual
// type of thing being pointed to might be a class that inherits from
// Student, so the computer must check and use the correct "calcGPA"
// function each time.
virtual double calcGPA();
};
class GradStudent : public Student
{
private:
char status;
public:
GradStudent (char = 'A');
void SetStatus(char);
// Same as above.
virtual double calcGPA();
};
class UnderGradStudent : public Student
{
private:
double CreditHrs;
double QualityPts;
public:
UnderGradStudent ( double = 0, double = 0);
void setCredits(int);
void setQuality(int);
// Same as above.
virtual double calcGPA ();
};
|
Now we can use a single "a" variable to refer to either an "UnderGradStudent" or a "GradStudent", and the program will check each time to see what kind of "Student" it is really looking at and call the correct function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
|
int main()
{
char contin, grade;
char StudentName[SIZE];
long StudentSSN;
int code, credits, points;
// Here is our pointer to a "Student". Keep in mind it may actually
// be a "GradStudent" or an "UnderGradStudent" as well.
// Initially, there is no student of any kind, so we'll set it to NULL.
Student* a = NULL;
do
{
cout << "Enter 1 for undergrad.\nEnter 2 for grad." << endl << "-->";
cin >> code;
if (code == 1)
{
a = new UnderGradStudent;
cin.ignore();
cout << "Enter Student name: ";
cin.getline(StudentName, SIZE);
a->setName(StudentName); // Notice how we now use "->" instead of "."
...
// And so on. Don't print anything here. Just get the student information from the user.
}
else if (code == 2)
{
a = new GradStudent;
...
// Same as above. Just get the grad student information and don't print anything.
}
// Don't let the user get away with entering an invalid answer...
else continue;
// Now we use our special magic. The compiler would normally think that we are looking
// at a "Student", but remember we told it that it might be an "UnderGradStudent"
// or a "GradStudent", so the compiler will automatically check that it calls the correct
// function:
cout << "\nName: " << a->getName() << " SSN: " << a->getSSN() << " GPA: "
<< setiosflags(ios::fixed | ios::showpoint) << setprecision(2) << a->calcGPA() << endl;
// Remember also that anything allocated with new must be deallocated as well
delete a;
a = NULL;
cout << "Continue?: ";
cin >> contin;
cin.ignore();
cout << endl;
}
while (contin == 'y' || contin == 'Y');
// Please don't use system()
// http://www.cplusplus.com/forum/articles/7312/
cout << "Press ENTER to quit.";
cin.ignore( 1000, '\n' );
return 0;
}
|
You should see that the computer is smart enough to tell your student types apart, because you used virtual functions, which told the compiler that you want it to check to see what kind of thing you are using.
If you use the code above
without virtual functions, you would find that the computer didn't bother to check to see what kind if student it was
actually looking at, because it thinks you are pointing to a "Student" only. Hence, it would always print "Student calcGPA".
I hope this helps you understand better the point of inheritance and polymorphism (many forms -- or using virtual functions to tell the compiler to check to see exactly what kind of thing it is working with instead of assuming it is using the kind of thing it says it is).
:-)