Why use polymorphism for grading activities?

  • Thread starter yungman
  • Start date
In summary,The PassFailActivity object has a minimum passing score of 70 and a points each value of 100.
  • #1
yungman
5,718
240
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!
 
Technology news on Phys.org
  • #2
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
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
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
That's why I like the shape class example. It's simple, it's familiar and it makes sense.
 
  • Like
Likes yungman
  • #7
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
jedishrfu said:
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
yungman said:
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.
yungman said:
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
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
Mark44 said:
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
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
jedishrfu said:
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!

ThanksBTW, Jave is so close to C++ I swear I can follow your program!
 
  • #14
yungman said:
I wonder what happened to the book, it gone downhill fast in the last two chapters.
jedishrfu said:
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
Mark44 said:
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
His bio might explain the reason the book isn't up to best practices:

https://www.haywood.edu/news/2012-05-31/tony-gaddis-hcc-business-administration-and-electronic-data-processing-graduate-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
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
Mark44 said:
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
yungman said:
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.
yungman said:
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.
yungman said:
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
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
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
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
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
yungman said:
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.
yungman said:
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
yungman said:
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
  • #26
For someone who doesn't like overloading, you sure use it a lot - over forty times!

Just out of curiosity, why are you using "\n" and not << endl?
 
  • #27
Vanadium 50 said:
For someone who doesn't like overloading, you sure use it a lot - over forty times!

Just out of curiosity, why are you using "\n" and not << endl?
I guess if it is standard overloading from C++, it's more reliable. It's so easy to make mistake writing overloading. Remember, I don't have real life programming experience, Gaddis book gone down hill in overloading and I might not see the whole picture. Like this Polymorphism chapter. After I learn and understand Polymorphism from you guys the last day or two, then I read back the book and it really misses the point. I just want to make sure I am not missing on overloading also.

Ha ha! I am a lazy typer! Mostly I use "\n\n", sure beats endl<<endl ! That's the reason you guys complained a lot on my programs I use names that is not making sense because I cut all the names short! I have to make the name longer to ask questions here now.
 
  • #28
yungman said:
Off the topic a little, I know we gone through the overloading before, I still not convinced overloading is very useful,... I just don't see the clear usefulness and clear making it easier to update programs
Most programming work is not the initial writing of the program, but rather maintaining it, using it, modifying it, and keeping everything straight later. Here is a nightmare scenario of a mythical world without overloading (suggested by Franz Kafka).
Suppose you have written a huge program for simulating a fighter airplane. You have written thousands of functions like getMach, getAltitude, etc. Now your boss gives you a huge official Air Force program to integrate for the air data system and it has thousands of functions like getMach, getAltitude, etc. Without overloading you can't safely use it without changing all the function calls so they are distinguishable from yours. After you have changed all those function names to airDataGetMach, airDataGetAltitude, etc., you go to your boss and he says that your next task is to integrate a massive Northrop Grumman model of the inertial navigation system that has thousands of functions getMach, getAltitude, etc. After you take care of all that, your code is reviewed and the reviewers want to see what you changed in the air data and inertial navigation models. You show them a list of millions of changes and try to convince them that you really didn't change anything. You will then know the value of overloading. With overloading, the compiler helps to keep track of things for you to call the right function without having to change function names everywhere (in theory, anyway).
 
Last edited:
  • Like
Likes yungman
  • #29
FactChecker said:
With overloading, the compiler helps to keep track of things for you to call the right function without having to change function names everywhere (in theory, anyway).
Just to be clear, I believe yungman's complaint was about overloading operators, not function overloads.
 
  • #30
Thanks Mark and FactChecker, That gives me a better feel what's going on in the big picture. I cannot say I fully understand what you two said, BUT it's definitely sounds like a much more legitimate reason than just what I saw in the book that only making operations look simpler when writing in main, like a+b, a/b for objects.

I am glad you guys took the time to explain to me on Polymorphism, or else when I look at the book, it really looked stupid!

thanks
 
  • #31
Mark44 said:
Just to be clear, I believe yungman's complaint was about overloading operators, not function overloads.
Some statements in that post specified operators, but others didn't, including the first statement. So I wasn't sure about that. I have a particular dislike for operator overloading but I can also see the advantage in the judicious use of it.
 
  • #32
@Youngman, one concept that is worth learning is the DRY principle. It seems sometimes you are asking us why we need a DRY solution when we can easily make a simple WET solution.

DRY solutions sometimes take more careful thought, engineering, and initial effort, than WET solutions, but there are a lot of advantages. The quick and easy route isn't always the best route.

Don't repeat yourself (DRY, or sometimes do not repeat yourself) is a principle of software development aimed at reducing repetition of software patterns,[1] replacing it with abstractions or using data normalization to avoid redundancy.
...
When the DRY principle is applied successfully, a modification of any single element of a system does not require a change in other logically unrelated elements. Additionally, elements that are logically related all change predictably and uniformly, and are thus kept in sync.
...
Violations of DRY are typically referred to as WET solutions, which is commonly taken to stand for "write every time", "write everything twice", "we enjoy typing" or "waste everyone's time". [4][5][6]

https://en.wikipedia.org/wiki/Don't_repeat_yourself
 
  • #33
Jarvis323 said:
@Youngman, one concept that is worth learning is the DRY principle. It seems sometimes you are asking us why we need a DRY solution when we can easily make a simple WET solution.

DRY solutions sometimes take more careful thought, engineering, and initial effort, than WET solutions, but there are a lot of advantages. The quick and easy route isn't always the best route.
https://en.wikipedia.org/wiki/Don't_repeat_yourself
Actually I am more looking for reason why using certain things in a macro point of view, DRY or WET is secondary. I won't be convince just saving a few lines only.

Remember I don't know real life programming, all I get in the book is how to make things "looks" easier using overloading. And even in Polymorphism, it's not very clear...Until reading the post from Mark and jedishrfu giving example in the big picture how Polymorphism really help to add more classes and use the same commands to call functions. That's when I really see the usefulness of Polymorphism.

For example, I can write a base class Travel, then I can have derived class "byWalk", "byDriving", "byPlane", "byRocket" etc. Then I can have member function go(destination) in every one of them. I then use a Travel*tptr to do tptr->go() to point to anyone of the derived class.

Say if you call go(mars) in byRocket class, you return (cout << " call Elon Mask" << endl); If you call go(mars) in byWalk class, return (cout << :biggrin: :biggrin::biggrin: << endl;). You can add more derived classes as will and will not affect all the existing ones. This is the key and most convincing reason for me, forget DRY or WET. Even if I have to write 100 extra lines of code to make it happens, it's worth it.I really want to hear about the real life usefulness of overloading in the macro world, not just make it easier to read. I can care less whether I write a few more or few less lines.
 
  • #34
yungman said:
Even if I have to write 100 extra lines of code to make it happens, it's worth it.
yungman said:
I really want to hear about the real life usefulness of overloading in the macro world, not just make it easier to read. I can care less whether I write a few more or few less lines.
You need to be more specific -- I believe you are talking about overloaded operators, right?

From post #25, which you might have missed...
Mark44 said:
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.
... and doesn't need to retype the code that is present in the overloaded operator.

Repeating myself, you seem to be making a big deal unnecessarily about overloaded operators, thinking that they are overly complicated. They really are just functions with a slight twist, mostly about whether they should return an object or a reference to an object.
 
  • Like
Likes Vanadium 50 and yungman
  • #35
I am excited about C++ for once! All of a sudden, things seems to come together, I am starting to see the reason of all these fancy stuffs. Stuffs starting to make sense( not in the sense of knowing how to use them, more the REASON for using them).

Chapter 15 has been going quite smooth other than the array of pointers that hung me up a little( it's resolved). About 14 more pages to go in this last chapter and I am done with the Brief version of Gaddis from cover to cover! It's time to chill the champion! Reaching a mile stone, ready for the next chapter. Thank to all you guys helping.

It's getting easier lately, the first 3 months was the hardest, those were the dark days when I didn't understand the names, the terms. Read online, didn't understand, when you guys tried to explain, didn't understand. Now, I am a lot more familiar with the names, things are just a lot easier even the subject gets a lot harder as it progresses.

Thanks
 
  • Like
Likes FactChecker

Similar threads

  • Programming and Computer Science
Replies
23
Views
1K
  • Programming and Computer Science
Replies
17
Views
1K
  • Programming and Computer Science
2
Replies
35
Views
2K
  • Programming and Computer Science
3
Replies
89
Views
4K
  • Programming and Computer Science
Replies
5
Views
872
Replies
10
Views
951
  • Programming and Computer Science
3
Replies
75
Views
4K
  • Programming and Computer Science
Replies
1
Views
640
  • Programming and Computer Science
Replies
6
Views
896
Replies
10
Views
2K
Back
Top