Why using polymophism Here?

  • Thread starter yungman
  • Start date
  • #1
5,335
170
I am studying polymorphism I just don't understand the purpose in this example from the book:
C++:
#include <iostream>
#include <iomanip>
using namespace std;
class GradedActivity
{protected:  double score;   // To hold the numeric score
public:
    GradedActivity()
    {   score = 0.0;
        cout<<"\t GradedActivity, Default Constructor, score = "<<score<<"\n\n";
    }
    GradedActivity(double s)
    {   score = s;
        cout << "\t GradedActivity, Constructor1, score = " << score << "\n\n";
    }
    void setScore(double s)
    {   score = s;
     cout << "\t GradedActivity setscore, score = " << score << "\n\n";
    }
    double getScore() const
    {   return score;
        cout << "\t GradedActivity getscore, score= " << score << "\n\n";}
    virtual char getLetterGrade() const//declare virtual
    {   char letterGrade; // To hold the letter grade
        if (score > 89)letterGrade = 'A';
        else if (score > 79) letterGrade = 'B';
        else if (score > 69) letterGrade = 'C';
        else if (score > 59)letterGrade = 'D';
        else  letterGrade = 'F';
        return letterGrade;
    }
};
class PassFailActivity : public GradedActivity
{protected:  double minPassingScore;    // Minimum passing score.
public:
    PassFailActivity() : GradedActivity()
    { minPassingScore = 0.0;
      cout<<"\t PassFailActivity Default Con, minPassingScore= "<<minPassingScore<<"\n\n";   
    }
    PassFailActivity(double mps) : GradedActivity()
    { minPassingScore = mps;
      cout << "\t PassFailActivity Const1, minPassingScore= "<<minPassingScore<<"\n\n";    
    }
    void setMinPassingScore(double mps)
    { minPassingScore = mps;
      cout << "\t PassFailActivity setMinPassingScore, minPassingScroe= "<<minPassingScore<<"\n\n";
    
    }
    double getMinPassingScore() const
    {   cout<<"\t PassFailActivity getMinPassingScore: "<<minPassingScore<<"\n\n";
        return minPassingScore;
    }
    virtual char getLetterGrade() const//Declare virtual
    {   char letterGrade;
        if (score >= minPassingScore) letterGrade = 'P';
        else letterGrade = 'F';
        cout<<"\t PassFailActivity getLetterGrade, letterGrade= "<<letterGrade<<"\n\n";
        return letterGrade;
    }
};
class PassFailExam : public PassFailActivity
{
private: int numQuestions; double pointsEach; int numMissed;
public:  PassFailExam() : PassFailActivity()
    {   cout << "\t PassFailExam Default Constructor: \n\n";
        numQuestions = 0;  pointsEach = 0.0;    numMissed = 0;
    }
    PassFailExam(int questions, int missed, double mps) :PassFailActivity(mps)
    {   cout << "\t PassFailExam Constructor 3: \n\n";
        set(questions, missed);}
    void set(int questions, int missed)
    {   double numericScore;
        numQuestions = questions;
        numMissed = missed;
        pointsEach = 100.0 / numQuestions;
        numericScore=100.0-(missed*pointsEach);
         cout << "\t PassFailExam set, numQuestion= "<<numQuestions<<
          ", numMissed= "<<numMissed<<", pointsEach= "<<pointsEach<<"\n\n";
        setScore(numericScore);
    }
    double getNumQuestions() const
    { cout << "\t PassFailExam  getNumQuestions: " << numQuestions<<"\n\n";
      return numQuestions;
    }
    double getPointsEach() const
    { cout << "\t PassFailExam  getPointsEach: " << pointsEach << "\n\n";
      return pointsEach;
    }
    int getNumMissed() const
    { cout << "\t PassFailExam  getNumMissed: " << numMissed << "\n\n";
      return numMissed;
    }
};
void displayGrade(const GradedActivity& activity)//declare activity a GradeActivity object
{   cout<<setprecision(1)<<fixed<<" The activity's numeric score is "<<activity.getScore()<<
         "\n The activity's letter grade is "<< activity.getLetterGrade() << "\n\n";}
int main()
{   PassFailActivity test(70);// Create a PassFailActivity object. Minimum passing score is 70.
    test.setScore(72); //score=72.
    displayGrade(test); return 0;//The letter grade should be 'P'.
}
The whole thing started with line 93:void displayGrade(const GradedActivity& activity) that declare object activity of GradedActivity class!!! Then it complain about it gives you a letter grade A, B, C..... Instead of P or F in class PassFailActivity!!

I simply change line 93 to void displayGrade(const PassFailActivity& activity) and I get P or F already.

Why declare virtual function in line 22 and 52 JUST to defeat the normal logical progression of the program(that is wrong)? This is like sticking worms into your behind and then take anti parasite medication to get rid of the worm!!! I don't know a better way to express how I feel!!!
 

Answers and Replies

  • #2
12,365
6,110
The idea here is that there are two kinds of courses:
- graded ones with grades of A, B, C, D, or F based on test scores
- pass-fail ones with grades of P, or F again based on test scores

When you ask for it to display course grades, you define a generic method called displayGrade() that calls the appropriate instance method based on the type of course it is ie the type of instance object passed in.

Since you passed in PassFailActivity instance object then the displayGrade() should call the PassFailActivity getLetterGrade() method which overrides the parent class's GradedActivity getLetterGrade().

Recall GradedActivity is a parent class to PassFailActivity so the displayGrade() method can accept either a GradedActivity instance or a PassFailActivity instance.

Since the getLetterGrade() method is defined as virtual then its an overrideable method().

More on virtual usage and how it subverts compiler errors for runtime errors if you're not careful.

https://www.fluentcpp.com/2020/02/21/virtual-final-and-override-in-cpp/

Personally, I would've designed this a bit differently using a common class like CourseActivity as the parent for both GradedActivity and PassFailActivity but that would introduce yet another class into the mix.
 
  • Like
Likes yungman and sysprog
  • #4
FactChecker
Science Advisor
Gold Member
5,971
2,290
If you understand the use of polymorphism in general (possibly from other examples), you should not expect every example in a textbook to convincingly motivate you. It might just be there to show you how to do something that you should already be motivated to do. Not all examples are self-contained, from conceptual motivation all the way through to implementation.
If you do not understand the use of polymorphism, here is a good youtube video to watch.
 
  • Like
Likes jedishrfu, yungman, sysprog and 1 other person
  • #5
5,335
170
Yes, I need to read more, the Gaddis book is not very good the last two chapters. You expect it would try to use examples that are little convincing. Like another example using a rectangle class ( 2D) for a cube.

thanks
 
  • #6
12,365
6,110
That's why I like the shape class example. It's simple, it's familiar and it makes sense.
 
  • Like
Likes yungman
  • #7
34,509
6,193
Here's what I consider a better example that focuses on polymorphism. It's based loosely on the example in post #1, but slimmed down to remove extra classes that get in the way of understanding, as well as any useless comments (i.e., comments that restate what the code is doing), and the unneeded output lines (a debugger can show you the values of any data members).

The code has a base class, Activity, and two classes that derive from it: GradedActivity and PassFailActivity. To get a feel for the polymorphism, look at lines 88 through 91 in main(), where I define pAct, a pointer to an Activity. It is first set with the address of a PassFailActivity object, and then later to a GradedActivity object. Even though the pointer refers to an Activity object, the compiler can detect which version of showGrade() to call.

Keep in mind that polymorphism means "many shapes." The idea here is that an Activity object can take multiple shapes.

Output from this code:
Code:
ActivityType: Pass/Fail Score: 72 Grade: P
ActivityType: Graded Score: 85 Grade: B
C++:
#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

class Activity
{
protected:
    int score;                         // Raw score
    char grade;                    // Letter grade: A - F or P/F
    string ActivityType;

public:
    Activity() {};
    virtual char getLetterGrade() { return grade; };
    virtual int getScore() { return score; };        
    virtual void setScore(int s) { score = s; };   
    void setGrade() {};                                              // Empty-body functions are   
    virtual void showGrade() {};                             //   implemented in derived classes.          
};

class GradedActivity: public Activity
{     
public:
    GradedActivity(int s = 0)
    {
        score = s;
        ActivityType = "Graded";
    }

   void setGrade()
    {
        if (score >= 90) grade = 'A';
        else if (score >= 80) grade = 'B';
        else if (score >= 70) grade = 'C';
        else if (score >= 60) grade = 'D';
        else  grade = 'F';
    }
    void showGrade()
    {       
        cout << "ActivityType: " << ActivityType << " Score: " << score << " Grade: " << grade << endl;
    }
};

class PassFailActivity : public Activity
{
protected:  int minPassingScore;   
public:
   
    PassFailActivity(int mps = 0)
    {
        minPassingScore = mps;       
        ActivityType = "Pass/Fail";
    }

    void setMinPassingScore(int mps)
    {
        minPassingScore = mps;       
    }

    int getMinPassingScore() const
    {
        return minPassingScore;
    }

    void setGrade()
    {
        if (score >= minPassingScore) grade = 'P';
        else grade = 'P';
    }   

    void showGrade()
    {
        cout << "ActivityType: " << this->ActivityType << " Score: " << score << " Grade: " << grade << endl;
    }
};

int main()
{
    PassFailActivity pfActivity(70);
    pfActivity.setScore(72);
    pfActivity.setGrade();
    GradedActivity gActivity(85);
    gActivity.setGrade();

    Activity *pAct = &pfActivity;
    pAct->showGrade();                                 // Polymorphism in action
    pAct = &gActivity;
    pAct->showGrade();                                 // Polymorphism in action here as well
};
 
  • Like
Likes jedishrfu and yungman
  • #8
5,335
170
Here's a nicer polymorphism example using the Shape class and specific shape classes:

https://www.tutorialspoint.com/cplusplus/cpp_polymorphism.htm
Thanks for the link, it is helpful, much better than in the book. The first two sentence explain much better than the first 10 pages of the Gaddis book. I still have some question actually not so much on Polymorphism. Here is the program from cplusplus:
C++:
[URL='http://tpcg.io/LSyBTc']Live Demo[/URL]
#include <iostream>
using namespace std;

class Shape {
   protected:
      int width, height;
   
   public:
      Shape( int a = 0, int b = 0){
         width = a;
         height = b;
      }
      int area() {
         cout << "Parent class area :" <<endl;
         return 0;
      }
};
class Rectangle: public Shape {
   public:
      Rectangle( int a = 0, int b = 0):Shape(a, b) { }
   
      int area () {
         cout << "Rectangle class area :" <<endl;
         return (width * height);
      }
};

class Triangle: public Shape {
   public:
      Triangle( int a = 0, int b = 0):Shape(a, b) { }
   
      int area () {
         cout << "Triangle class area :" <<endl;
         return (width * height / 2);
      }
};

// Main function for the program
int main() {
   Shape *shape;
   Rectangle rec(10,7);
   Triangle  tri(10,5);

   // store the address of Rectangle
   shape = &rec;
 
   // call rectangle area.
   shape->area();

   // store the address of Triangle
   shape = &tri;
 
   // call triangle area.
   shape->area();
 
   return 0;
}
My question is on line41, 46 and 52 where 41 declare pointer shape of Shape class. But line 46 and 52 declare pointer shape to point to address of rec and tri resp. I have no problem shape points to any object of class Shape, but rec and tri are objects of the Derived classes. Any way to make me more comfortable with this?

I also want to verify that this program only display "Parent class area" because shape is pointer of Shape class, that's the reason it is bound to the area() in the Shape class, not in the Rectangle or Triangle class. It is that simple. And putting virtual in front of the area() in the Shape class will tell the compiler to choose the area() in rec and tri because shape = &rec and shape = &tri in line 46 and 52 resp. Am I correct?



Still why use pointer shape? Why not using Rectangle *rect and Triangle *triangle as pointers and you never have to worry about virtual function and polymorphism. Is there a case that you actually have to use polymorphism and virtual function?

Thanks
 
Last edited:
  • #9
34,509
6,193
I have no problem shape points to any object of class Shape, but rec and tri are objects of the Derived classes. Any way to make me more comfortable with this?
A derived class contains all of the data members of the base class, and has access to the function members of the base class. If a derived class has function members that override the base class functions, those are the ones that are called if the object is an instance of a derived class.
Still why use pointer shape? Why not using Rectangle *rect and Triangle *triangle as pointers and you never have to worry about virtual function and polymorphism. Is there a case that you actually have to use polymorphism and virtual function?
With polymorphism, you can package up all of the attributes that are common to a variety of things in a base class, and include only the attributes that are specific to the derived classes. In the example you showed, a Shape is generic: it has a width, a height, and an area. By using polymorphism you can leverage the features that are common to a variety of shapes. The derived objects don't have to be cluttered up with their own data members--they can use the data that's in the base class.

If a base class has a virtual function, a derived class can redefine that function.

It would be simple to extend the program to other shapes such as trapezoids, parallelograms, circles, and so on.
 
  • #10
12,365
6,110
Another way to look at the problem would be to design a Shape object without polymorphism and see what you get.

In general, the alternative is to use a switch case inside each of the functions to accomplish what polymorphism accomplishes and you will quickly see why OO programmers prefer it so much.

Here's an example in Java (not guaranteed to work):

Java:
package my.unpoly.example;

import ...; // imports go here

class MyUnpolyExample {

    class Shape {

        public static final int RECT 1 = 1;
        public static final int TRIANGLE = 2;

        private int stype;
        private float width;
        private float height;

        public Shape(int stype, float width, float height) {
            this.type = type;
            this.width = width;
            this.height = height;
       }

        public float area() {
           float a;

            switch (stype) {
            case RECT:
                a = width * height;
                break;

            case TRIANGLE:
                a = 0.5 * width *height;
                break;

            default:
                // error
           }

            return a;
        }
    }

    public static int main(String args[]) {

        Shape s1 = new Shape(Shape.RECT,10,20);
        Shape s2 = new Shape(Shape.TRIANGLE,10,20);

        println("The RECT area is:"+s1.area());
        println("The TRIANGLE area is: "+s2.area();
    }

    private static void println(String s) {
        System.out.println(s);
    }

}
The problem the programmer will have is adding new shapes affects every method in the Shape class.

In addition, folks using the Shape class will need access to the source code for their shapes and the list goes on...

Polymorphism just hides the method switching under the covers as part of the language implementation.
 
  • Like
Likes yungman
  • #11
5,335
170
Here's what I consider a better example that focuses on polymorphism. It's based loosely on the example in post #1, but slimmed down to remove extra classes that get in the way of understanding, as well as any useless comments (i.e., comments that restate what the code is doing), and the unneeded output lines (a debugger can show you the values of any data members).

The code has a base class, Activity, and two classes that derive from it: GradedActivity and PassFailActivity. To get a feel for the polymorphism, look at lines 88 through 91 in main(), where I define pAct, a pointer to an Activity. It is first set with the address of a PassFailActivity object, and then later to a GradedActivity object. Even though the pointer refers to an Activity object, the compiler can detect which version of showGrade() to call.

Keep in mind that polymorphism means "many shapes." The idea here is that an Activity object can take multiple shapes.

Output from this code:
Code:
ActivityType: Pass/Fail Score: 72 Grade: P
ActivityType: Graded Score: 85 Grade: B
C++:
#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

class Activity
{
protected:
    int score;                         // Raw score
    char grade;                    // Letter grade: A - F or P/F
    string ActivityType;

public:
    Activity() {};
    virtual char getLetterGrade() { return grade; };
    virtual int getScore() { return score; };       
    virtual void setScore(int s) { score = s; };  
    void setGrade() {};                                              // Empty-body functions are  
    virtual void showGrade() {};                             //   implemented in derived classes.         
};

class GradedActivity: public Activity
{    
public:
    GradedActivity(int s = 0)
    {
        score = s;
        ActivityType = "Graded";
    }

   void setGrade()
    {
        if (score >= 90) grade = 'A';
        else if (score >= 80) grade = 'B';
        else if (score >= 70) grade = 'C';
        else if (score >= 60) grade = 'D';
        else  grade = 'F';
    }
    void showGrade()
    {      
        cout << "ActivityType: " << ActivityType << " Score: " << score << " Grade: " << grade << endl;
    }
};

class PassFailActivity : public Activity
{
protected:  int minPassingScore;  
public:
  
    PassFailActivity(int mps = 0)
    {
        minPassingScore = mps;      
        ActivityType = "Pass/Fail";
    }

    void setMinPassingScore(int mps)
    {
        minPassingScore = mps;      
    }

    int getMinPassingScore() const
    {
        return minPassingScore;
    }

    void setGrade()
    {
        if (score >= minPassingScore) grade = 'P';
        else grade = 'P';
    }  

    void showGrade()
    {
        cout << "ActivityType: " << this->ActivityType << " Score: " << score << " Grade: " << grade << endl;
    }
};

int main()
{
    PassFailActivity pfActivity(70);
    pfActivity.setScore(72);
    pfActivity.setGrade();
    GradedActivity gActivity(85);
    gActivity.setGrade();

    Activity *pAct = &pfActivity;
    pAct->showGrade();                                 // Polymorphism in action
    pAct = &gActivity;
    pAct->showGrade();                                 // Polymorphism in action here as well
};
Thanks so much in explaining why I can use shape to point to address of rec and tri. That's exactly what I need. The book is NOT good in explaining Polymorphism at all, Your post and post by jedishrfu really explained much better than all the pages I read in the book. I wonder what happened to the book, it gone downhill fast in the last two chapters.

Thanks
 
  • #12
12,365
6,110
They were in a hurry to get it published before the next big thing in programming. That or the author was tired of writing and rewriting it. Early programming books were often plagued with mistakes in the listings. Finally some authors automated the writing process by programmatically incorporating code that they had debugged and was guaranteed to compile. They could then claim they had error-free listings.
 
  • #13
5,335
170
Another way to look at the problem would be to design a Shape object without polymorphism and see what you get.

In general, the alternative is to use a switch case inside each of the functions to accomplish what polymorphism accomplishes and you will quickly see why OO programmers prefer it so much.

Here's an example in Java (not guaranteed to work):

Java:
package my.unpoly.example;

import ...; // imports go here

class MyUnpolyExample {

    class Shape {

        public static final int RECT 1 = 1;
        public static final int TRIANGLE = 2;

        private int stype;
        private float width;
        private float height;

        public Shape(int stype, float width, float height) {
            this.type = type;
            this.width = width;
            this.height = height;
       }

        public float area() {
           float a;

            switch (stype) {
            case RECT:
                a = width * height;
                break;

            case TRIANGLE:
                a = 0.5 * width *height;
                break;

            default:
                // error
           }

            return a;
        }
    }

    public static int main(String args[]) {

        Shape s1 = new Shape(Shape.RECT,10,20);
        Shape s2 = new Shape(Shape.TRIANGLE,10,20);

        println("The RECT area is:"+s1.area());
        println("The TRIANGLE area is: "+s2.area();
    }

    private static void println(String s) {
        System.out.println(s);
    }

}
The problem the programmer will have is adding new shapes affects every method in the Shape class.

In addition, folks using the Shape class will need access to the source code for their shapes and the list goes on...

Polymorphism just hides the method switching under the covers as part of the language implementation.
Good explanation and convincing. You don't have to write extra for new shapes, just the class of the new shape and integrate into the existing rectangle, triangle and other shapes.

I am catching up with all the replies!!

Thanks


BTW, Jave is so close to C++ I swear I can follow your program!!
 
  • #14
34,509
6,193
I wonder what happened to the book, it gone downhill fast in the last two chapters.
They were in a hurry to get it published before the next big thing in programming. That or the author was tired of writing and rewriting it.
IMO, Gaddis is just "phoning it in" as far as the last two chapters are concerned. His first edition was published back in '98, and seems to be not too different from the 5th edition that I think yungman is using.
 
  • Like
  • Haha
Likes yungman and jedishrfu
  • #15
5,335
170
IMO, Gaddis is just "phoning it in" as far as the last two chapters are concerned. His first edition was published back in '98, and seems to be not too different from the 5th edition that I think yungman is using.
I am using the 6th( as if it makes a difference!!). My question is whether chapter 16 to 20 in his 6th edition complete book is bad also? That will be my next thing to study. They are on data structure. I already have the Schaum's book on Data Structure with C++, I wonder I should switch book. Just I have the CD for Gaddis that helps.

It's so ridiculous that the first two sentences of cplusplus together with your post and jedishrfu post explained so much clearer that all the pages Gaddis chapter I read so far......And believe me, I read it multiple times already. It's too bad, the first 13 Chapters are top notch. The examples you and jedishrfu gave are spot on. Even the one from cplusplus is good enough to get the point.

Thanks
 
  • #16
12,365
6,110
His bio might explain the reason the book isn't up to best practices:

https://www.haywood.edu/news/2012-0...uate-selected-as-outstanding-alumni-recipient.

He's been teaching at HCC mostly. I don't see where he was doing software development in C++ in the private sector to really hone his skills so his code is likely more academic than practical.

His books show a breadth of knowledge across several major languages like Java, C++, and Python but from an academic viewpoint.

https://www.pearson.com/us/higher-e...evel-for-Gaddis-C-Access-Card/PGM2474295.html

The link above has a link of him describing his books and courses.

In contrast, @Mark44 worked as a professional programmer and suffered through Symonyi notation which qualifies him to be an expert in C/C++ development.

In my case, I did a lot of C/C++ development and taught IBM's in-house C/C++ course in the mid-80's and then switched to Java around 1998 as IBM got into using it in a big way.

So you could say we are battle hardened by C/C++ and OO paradigms.
 
  • Like
Likes yungman
  • #17
5,335
170
But I still have to say the first 13 chapters of Gaddis is very good. I have a few books on C++, nothing even come close. The difference is all the other books start out kind of expecting you to know a little about programming. For me, it's the first language in 40 years. Hind sight tells me I really didn't know anything about programming when I first started C++ half a year ago. what I learned before doesn't really help with the names and terms. So I was really starting from scratch. Gaddis is the ONLY book that held my hands to ease in ( of cause with all you guys' help). For any beginners that want to learn C++, I would still recommend Gaddis.....up to chapter 13.

Gaddis does give a lot more examples in the book and provide CD that really helps. Just situation change as you gain knowledge. Hopefully, I am not consider a green horn at this point as I almost the whole book from cover to cover. I find it much easier to read online now, I was so loss the first two or three months, even when you guys explained to me, a lot of things I didn't even understand what you said. Gaddis really tied me over during those dark days.


Oh, I just notice, I still have not watch the youtube video provided by FactChecker!!!
 
Last edited:
  • #18
5,335
170
Here's what I consider a better example that focuses on polymorphism. It's based loosely on the example in post #1, but slimmed down to remove extra classes that get in the way of understanding, as well as any useless comments (i.e., comments that restate what the code is doing), and the unneeded output lines (a debugger can show you the values of any data members).

The code has a base class, Activity, and two classes that derive from it: GradedActivity and PassFailActivity. To get a feel for the polymorphism, look at lines 88 through 91 in main(), where I define pAct, a pointer to an Activity. It is first set with the address of a PassFailActivity object, and then later to a GradedActivity object. Even though the pointer refers to an Activity object, the compiler can detect which version of showGrade() to call.

Keep in mind that polymorphism means "many shapes." The idea here is that an Activity object can take multiple shapes.

Output from this code:
Code:
ActivityType: Pass/Fail Score: 72 Grade: P
ActivityType: Graded Score: 85 Grade: B
C++:
#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

class Activity
{
protected:
    int score;                         // Raw score
    char grade;                    // Letter grade: A - F or P/F
    string ActivityType;

public:
    Activity() {};
    virtual char getLetterGrade() { return grade; };
    virtual int getScore() { return score; };    
    virtual void setScore(int s) { score = s; };
    void setGrade() {};                                              // Empty-body functions are
    virtual void showGrade() {};                             //   implemented in derived classes.      
};

class GradedActivity: public Activity
{ 
public:
    GradedActivity(int s = 0)
    {
        score = s;
        ActivityType = "Graded";
    }

   void setGrade()
    {
        if (score >= 90) grade = 'A';
        else if (score >= 80) grade = 'B';
        else if (score >= 70) grade = 'C';
        else if (score >= 60) grade = 'D';
        else  grade = 'F';
    }
    void showGrade()
    {   
        cout << "ActivityType: " << ActivityType << " Score: " << score << " Grade: " << grade << endl;
    }
};

class PassFailActivity : public Activity
{
protected:  int minPassingScore;
public:

    PassFailActivity(int mps = 0)
    {
        minPassingScore = mps;   
        ActivityType = "Pass/Fail";
    }

    void setMinPassingScore(int mps)
    {
        minPassingScore = mps;   
    }

    int getMinPassingScore() const
    {
        return minPassingScore;
    }

    void setGrade()
    {
        if (score >= minPassingScore) grade = 'P';
        else grade = 'P';
    }

    void showGrade()
    {
        cout << "ActivityType: " << this->ActivityType << " Score: " << score << " Grade: " << grade << endl;
    }
};

int main()
{
    PassFailActivity pfActivity(70);
    pfActivity.setScore(72);
    pfActivity.setGrade();
    GradedActivity gActivity(85);
    gActivity.setGrade();

    Activity *pAct = &pfActivity;
    pAct->showGrade();                                 // Polymorphism in action
    pAct = &gActivity;
    pAct->showGrade();                                 // Polymorphism in action here as well
};

Hi Mark

I stepped through your program to make sure I follow every step. I have a few questions:

1)You put virtual on line 17 and 18, program never execute these two lines, why you put virtual?

2) Line 20, you did not put virtual, but you did not put anything inside { } to force the compiler to go to the derived class to execute the derived class function. Is this an example of not using virtual but still forcing the compiler to go to the derived class?

3) Follow up in question 2), in line 21, you did put virtual, also empty { }, can you clarify this? Is this similar to what cplusplus called PURE VIRTUAL FUNCTION?


I understand exactly what you try to show me, I just want to clarify exactly what you are doing. I am not letting anything go. Now I have to watch the youtube video from FactChecker. these are GOOD STUFFS.

Thanks
 
Last edited:
  • #19
34,509
6,193
1)You put virtual on line 17 and 18, program never execute these two lines, why you put virtual?
I went through several iterations of this program, as I was a bit rusty on polymorphism. There's really no need for getLetterGrade() and getScore() to be virtual. Derived classes can use the base-class versions of these functions.
2) Line 20, you did not put virtual, but you did not put anything inside { } to force the compiler to go to the derived class to execute the derived class function. Is this an example of not using virtual but still forcing the compiler to go to the derived class?
I should have added virtual for the setGrade() function, similar to what I did with showGrade(). The compiler seems to have done what I wanted it to do, even though I hadn't specified it.
Here's the revised version of the Activity class.
C++:
class Activity
{
protected:
    int score;
    char grade;
    string ActivityType;
public:
    Activity() {};
    char getLetterGrade() { return grade; };
    int getScore() { return score; };
    void setScore(int s) { score = s; };
    virtual void setGrade() {};            // Implemented in derived classes
    virtual void showGrade() {};           // Implemented in derived classes   
};
I didn't change anything in the two derived classes. Output is still the same as I posted before.
3) Follow up in question 2), in line 21, you did put virtual, also empty { }, can you clarify this? Is this similar to what cplusplus called PURE VIRTUAL FUNCTION?
No, the declaration of a pure virtual function has no braces at all, but would instead have = 0 in their place. It would look like this:
void showGrade() = 0;
The cplusplus.com tutorial on polymorphism has a fairly good discussion of this, better than the VS docs - Polymorphism - C++ Tutorials (cplusplus.com) See the section on Abstract base classes.
 
Last edited:
  • Like
Likes yungman
  • #20
5,335
170
Thanks FactChecker:
Thank you for the link, it's very helpful. I actually wrote a program using the idea. Just very simple calculating the area of triangle, rectangle and circle. Please verify I am correct. The idea is I can use the Area *aptr pointer to point to tri, rec and cir object, then go to their respective setArea() to display the area that calculated from different formula.

C++:
#include <iostream>
using namespace std;
class Area
{
public:
    double area;
    Area() { }
    virtual double setArea() { return 0; }
};
class Triangle : public Area
{
protected: double side1, side2, side3;
public:
    Triangle(double s1, double s2, double s3)
    {
        side1 = s1;
        side2 = s2;
        side3 = s3;
    }
    double setArea()
    { return (side1 * side2 * side3);}//Wrong formula, just to show it works.
};
class Rectangle : public Area
{
protected:   double len, wid;
public:
    Rectangle(double length, double width)
    {
        len = length;
        wid = width;
    }
    double setArea() { return (len * wid); }
};
class Circle : public Area
{
protected: double r;
public:
    Circle(double radius) { r = radius; }
    double setArea() { return (r * r * 3.14); }
};

int main()
{
    Area* aptr;
    Triangle tri(10, 7, 7);
    Rectangle rec(3, 4);
    Circle cir(5);
    aptr = &tri;
    cout << " Area of triangle: " << (*aptr).setArea() << "\n\n";
    aptr = &rec;
    cout << " Area of rectangle: " << (*aptr).setArea() << "\n\n";
    aptr = &cir;
    cout << " Area of circle: " << (*aptr).setArea() << "\n\n";
    return 0;
}

Note that the formula and answer for area of triangle is WRONG, I am too lazy to go through cosine law to calculate area of triangle, I just do side1*side2*side3!! I want to work with Polymorphism, not math calculation.

Thanks
 
Last edited:
  • #21
34,509
6,193
I get it that your focus is on the code and not the math, but you don't need the cosine law. Heron's Formula works just fine -- Heron's formula - Wikipedia .

A minor problem in your base class and the derived classes is the setArea() member function. A "setter" should merely set the value of a data member, so probably should be a void function with no return statement. A "getter" should return the value of a data member.

Note that I'm calling this function getArea(). Because I'm using a standard library function, sqrt(), you'll need to include the cmath or math.h header.

Not all combinations of three side lengths are possible for a triangle. For example, side lengths of 1, 2 and 5 don't form a triangle. I didn't include any code to prevent possible erroneous results, but it's not too hard to check for bad results.
C++:
  double getArea()
{ 
    // Calculate area of triangle using Heron's formula
    double perim = side1 + side2 + side3;         // Perimeter of triangle
    double temp = perim * (perim - side1) * (perim - side2) * (perim - side3));
    return sqrt(temp}; 
}
 
  • Like
Likes yungman
  • #22
5,335
170
Thanks Mark,

I am just being very lazy and want to only concentrate on using Area pointers to point to setArea() that contain different formula and using different number of variables.

I am just excited I wrote this all on my own. Now this is fun, first time learning I can use the same pointer and call functions of different classes. That I can add new classes at will like Polygon class, square class, hexagon class etc. and do things for individual using the same pointer. That I don't have to CHANGE the existing codes and just simply adding new classes and add a few news lines in main() without touching anything else. This is a big deal.

I can see how this make adding and updating program with much less effort.

Off the topic a little, I know we gone through the overloading before, I still not convinced overloading is very useful, You and others gave example about overloading make the formula easier to read like a*b of objects etc. But I still see there is a lot of custom overhead to write the operator overloading, I just don't see the clear usefulness and clear making it easier to update programs like Polymorphism. Is there anything I missed on overloading? This is chapter 14 of Gaddis and hind sight telling me that it goes down hill on overloading.

Thanks
 
Last edited:
  • #23
5,335
170
Hi Mark
I played with your program and use pointer pAct more:
C++:
int main()
{   Activity* pAct;//create pointer of Activity class
    PassFailActivity pfActivity(70);
    GradedActivity gActivity(85);
//pointer pAct points to pfActivity
    pAct = &pfActivity;
    pAct->setScore(72);
    pAct->setGrade();
    pAct->showGrade();
//pointer pAct points to gActivity
    pAct = &gActivity;
    pAct->setScore(95);//Change the score
    pAct->setGrade();
    pAct->showGrade(); // Polymorphism in action
};
Output is
Code:
ActivityType: Pass/Fail Score: 72 Grade: P
ActivityType: Graded Score: 95 Grade: A
Now, everything is done by the pointer pAct to setGrade() and showGrade(). This is good stuff.
 
  • #24
34,509
6,193
That I can add new classes at will like Polygon class, square class, hexagon class etc. and do things for individual using the same pointer. That I don't have to CHANGE the existing codes and just simply adding new classes and add a few news lines in main() without touching anything else. This is a big deal.
Now you're getting the idea of what polymorphism is all about.
I still not convinced overloading is very useful, You and others gave example about overloading make the formula easier to read like a*b of objects etc. But I still see there is a lot of custom overhead to write the operator overloading,
But you only have to write the overload once, rather than repeat it in every operation you do. This is basically why we write functions rather then inserting the same code inline every time we want to do the operation. Again, operator overloads are pretty much the same idea as functions.
 
  • Like
Likes yungman and sysprog
  • #25
34,509
6,193
Off the topic a little, I know we gone through the overloading before, I still not convinced overloading is very useful,
One thing I don't think you realize is that there are different programmer roles. In one role, a programmer develops classes (or libraries or APIs); a different end-user programmer potentially would use those classes. In contrast, you've been writing the whole program -- whatever classes are called for, and main() where the classes get instantiated and used. In real life, with large programs, a team of developers produces a set of classes that end users use to create their own programs. Typically the development team comes up with features to make the API more attractive to the end-user buyers. Including the convenience of overloaded operators is part of what could make the API more attractive.

It doesn't matter if the API developer has to type a few more lines of code to add a feature like an operator overload. What matters is that the convenience to the end-user, who doesn't have to re-invent the wheel each time he needs to perform some operation.
 
  • Like
Likes yungman, FactChecker and sysprog

Related Threads on Why using polymophism Here?

Replies
2
Views
2K
  • Last Post
Replies
1
Views
2K
  • Last Post
Replies
3
Views
4K
  • Last Post
Replies
4
Views
810
Replies
10
Views
938
Replies
2
Views
676
  • Last Post
Replies
1
Views
3K
  • Last Post
Replies
19
Views
14K
  • Last Post
Replies
1
Views
1K
  • Last Post
Replies
22
Views
3K
Top