Understanding Inheritance and Polymorphism in C++

  • Context: C/C++ 
  • Thread starter Thread starter yungman
  • Start date Start date
  • Tags Tags
    Line
Click For Summary
SUMMARY

The discussion centers on the use of constructors in C++ and specifically addresses the line of code PassFailExam exam(questions, missed, minPassing);. This line is a constructor call that initializes an object of the PassFailExam class with three parameters, which is a shorthand for declaring the object and then calling a constructor. Participants clarify that this line does not represent a function call but rather a direct instantiation of the object using the defined constructor. The conversation also touches on the importance of understanding object-oriented programming concepts such as inheritance and polymorphism in C++.

PREREQUISITES
  • Understanding of C++ class and object syntax
  • Familiarity with constructors and destructors in C++
  • Knowledge of inheritance and polymorphism in object-oriented programming
  • Basic understanding of C++ standard input/output operations
NEXT STEPS
  • Study C++ constructors and destructors in detail
  • Learn about inheritance and polymorphism in C++ with practical examples
  • Explore C++ operator overloading, specifically the function call operator
  • Practice implementing and debugging C++ programs that utilize multiple constructors
USEFUL FOR

C++ developers, computer science students, and anyone looking to deepen their understanding of object-oriented programming concepts, particularly inheritance and polymorphism in C++.

yungman
Messages
5,741
Reaction score
291
Hi
There is one line of code I don't understand and I cannot find it in 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; }
    GradedActivity(double s)
    { score = s;  }
    void setScore(double s)
    { score = s; }
    double getScore() const
    { return score; }
    char getLetterGrade() const
    {  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;}
    PassFailActivity(double mps) : GradedActivity()
    {minPassingScore = mps;}
    void setMinPassingScore(double mps)
    {minPassingScore = mps; }
    double getMinPassingScore() const
    {return minPassingScore;}
    char getLetterGrade() const
    {
        char letterGrade;
        if(score >= minPassingScore) letterGrade = 'P';
        else letterGrade = 'F';
        return letterGrade;
    }
};
class PassFailExam : public PassFailActivity
{
private: int numQuestions; double pointsEach; int numMissed;
public:  PassFailExam() : PassFailActivity()
    {
      numQuestions = 0;
      pointsEach = 0.0;
      numMissed = 0;
    }
    PassFailExam(int questions, int missed, double mps):PassFailActivity(mps)
    {set(questions, missed); }
    void set(int questions, int missed)
    {
        double numericScore;
        numQuestions = questions;
        numMissed = missed;
        pointsEach = 100.0 / numQuestions;
        numericScore = 100.0 - (missed * pointsEach);
        setScore(numericScore);
    }
    double getNumQuestions() const
    { return numQuestions;}
    double getPointsEach() const
    {return pointsEach; }
    int getNumMissed() const
    { return numMissed; }
};
int main()
{   int questions;  int missed;  double minPassing;
    count << "How many questions are on the exam? "; cin >> questions;
    count << "How many questions did the student miss? ";cin >> missed;
    count << "Enter the minimum passing score for this test: ";cin >> minPassing;
    PassFailExam exam(questions, missed, minPassing);//What is this?
    count << fixed << setprecision(1)<< "\nEach question counts "
        << exam.getPointsEach() << " points.\n The minimum passing score is "
        << exam.getMinPassingScore() << "\n The student's exam score is "<<
        exam.getScore()<<"\n The student's grade is "<<exam.getLetterGrade()<<"\n\n";
    return 0;
}

It is line 76: PassFailExam exam(questions, missed, minPassing); I have no idea what this line is. Normally, when we declare an object we just write PassFailExam exam to declare exam is Object of PassFailExam class. But this one has 3 parameters. It is not a constructor declaration either. What is this? It's almost like a function that return an object of PassFailExam. Is this the case? I never seen this before.

Follow up question is if it's a function that return a class object, then you can call on the public member functions of the object like in line 77, 78,79? Like calling getScore().After this question, I finished Inheritance in the chapter, only Polymorphism left for the whole book. Inheritance seems quite straight forward...OR, am I missing something here? Just want to make sure it's that simple. Please let me know.

I read a few pages of Polymorphism, that seems much harder, I am not even sure I understand the few two pages yet!

Thanks
 
Last edited:
Technology news on Phys.org
From the code, it’s calling a PassFailExam constructor with three parameters To define an object called exam.

You could step through the code with a debugger to really see how it works.

http://pucitnc.yolasite.com/resources/Ch 15.pdf
 
  • Like
Likes   Reactions: Jarvis323, sysprog, yungman and 1 other person
jedishrfu said:
it’s calling a PassFailExam constructor with three parameters
which is defined on line 53.
 
  • Like
Likes   Reactions: Vanadium 50, Jarvis323, sysprog and 2 others
Oh, you mean it's a short hand form of two statements:
C++:
PassFailExam exam;//declaring exam an object of class PassFailExam
exam(questions, missed, minPassing) ;//Calling constructor with 3 parameters

I missed that. The fool of me!
That shows I am still not quite digested all the material yet! Now it looks so obvious! I need practice, not just keep learning and learning.

Thanks
 
Programmers have an eye for patterns and how languages work so even if we don't know the language we can figure stuff out.

However, languages with unfamiliar paradigms are much harder to figure out. These include:
- prolog
- lisp / clojure
- forth
- apl
- assembler

Most languages based on C are easier to figure out. Java is a good example where much of the OO stuff is like C++ but not exactly.
 
  • Like
Likes   Reactions: yungman, Jarvis323 and sysprog
yungman said:
Oh, you mean it's a short hand form of two statements:
C++:
PassFailExam exam;//declaring exam an object of class PassFailExam
exam(questions, missed, minPassing) ;//Calling constructor with 3 parameters
Not the same. The code you show above calls two different constructors, first the one defined on line 47, then the 3-argument one. This can be dangerous if the constructors increments some global counter of constructors called.
 
  • Like
Likes   Reactions: Vanadium 50 and yungman
Halc said:
Not the same. The code you show above calls two different constructors, first the one defined on line 47, then the 3-argument one. This can be dangerous if the constructors increments some global counter of constructors called.
Yes, I know, the first one call the default constructor then the second line call the constructor with 3 parameters. What I was saying, the end result is the same.

This is exactly what Jtbell put in his thread showing two different ways that called different constructors.
 
Last edited:
yungman said:
Oh, you mean it's a short hand form of two statements:
C++:
PassFailExam exam;//declaring exam an object of class PassFailExam
exam(questions, missed, minPassing) ;//Calling constructor with 3 parameters
Halc said:
Not the same. The code you show above calls two different constructors, first the one defined on line 47, then the 3-argument one.
Actually, the second statement doesn't invoke a constructor at all. Object exam has already been constructed in the first statement, and I'd be surprised if one could construct it again.

The second statement attempts to invoke the "function call operator" operator() on object exam. If you haven't defined operator() for class PassFailExam, the compiler spits out an error message. At least mine did when I tried something similar with my ThreeVector class:

C++:
    ThreeVector v;
    v (1, 2, 3);

I got the message error: type 'ThreeVector' does not provide a call operator v (1, 2, 3); which sent me scurrying to Google to find out about call operators, because I've never used them. If I've ever read about them before, I've forgotten it.
 
Last edited:
  • Like
Likes   Reactions: yungman
yungman said:
Oh, you mean it's a short hand form of two statements:
C++:
PassFailExam exam;//declaring exam an object of class PassFailExam
exam(questions, missed, minPassing) ;//Calling constructor with 3 parameters

No. I don't think the code you wrote should even compile.

Halc said:
Not the same. The code you show above calls two different constructors, first the one defined on line 47, then the 3-argument one.

Where is the second constructor?

yungman said:
Yes, I know, the first one call the default constructor then the second line call the constructor with 3 parameters.

Where is the second constructor?

yungman said:
This is exactly what Jtbell put in his thread showing two different ways that called different constructors.

It can't be.

The line PassFailExam exam(questions, missed, minPassing) is one construcor taking three parameters.

The line PassFailExam exam calls the default constructor.

Then the line exam(questions, missed, minPassing) is not a constructor. It is a call to a subroutine named exam, passing (by value) three parameters. Since there is no subroutine with the name exam, the compiler should give an error.
 
  • Like
Likes   Reactions: yungman
  • #10
I stand corrected. Should have compiled a little test first.

I remember needing to do exactly that and creating a member function to effectively reconstruct a 'used' object in place, which had different syntax. It effectively had the same end result as calling the destructor and constructor in sequence.
 
  • #11
Vanadium 50 said:
Then the line exam(questions, missed, minPassing) is not a constructor. It is a call to a subroutine named exam, passing (by value) three parameters. Since there is no subroutine with the name exam, the compiler should give an error.
If exam had not already been declared, that would be correct: the compiler would look for a function exam().

In this case, exam has already been declared as an object of class type PassFailExam, so the compiler looks for an operator() in that class.
 
  • Like
Likes   Reactions: yungman and Vanadium 50
  • #12
That's right. It will be invalid code for a different reason. :wink:
 
  • #13
Halc said:
I remember needing to do exactly that and creating a member function to effectively reconstruct a 'used' object in place, which had different syntax. It effectively had the same end result as calling the destructor and constructor in sequence.
Are you aware of the concept of an "in-place" constructor? It may be called other things too, but that's what I call it. You can recall a constructor by using placement new operator.

ala:
Code:
void * someMemoryChunk = malloc(sizeof(myClass));
myClass * ptr = new (someMemoryChunk) myClass();  //Essentially the same as calling new with two steps
ptr -> doStuffThatChangesMembers();
ptr->~myClass();  //destructor without deallocation
ptr = new (ptr) myClass();  //Reset it, new without allocation
ptr->~myClass();
free(someMemoryChunk);

//Works with stack objects the same way
myClass obj;
obj.doStuffThatChangesMembers();
obj.~myClass();
new (&obj) myClass();  //Reset it
Code like that is common in memory pools where you don't want to be constantly allocating and freeing memory. I will also occasionally use the 2 step construction process instead of a new for speed reasons, a new() allocation will attempt to find memory it can pack in a neat way, where malloc is less picky. Sometimes if you want something to run really really fast, you have to manage memory at an even lower level than C++'s functions.
 
  • #14
Thanks guys,

I just forgot about declaration of class object with arguments. It's spelled out clearly in chapter 13. I just have to read through that again. Blame on the old brain.
 
  • #15
I actually added lines to the original program to print out where the program goes and stepped through the program. I am absolutely surprised that in line101 to 105 , it actually executing BACKWARDS from exam.getLetterGrade() , moving back to exam.getScore() and then back...
C++:
    count << fixed << setprecision(1) << "\nEach question counts "
        << exam.getPointsEach() << " points.\n The minimum passing score is "
        << exam.getMinPassingScore() << "\n The student's exam score is " <<
        exam.getScore() << "\n The student's grade is " << exam.getLetterGrade() << "\n\n";
    return 0;
C++:
#include <iostream>
#include <iomanip>
using namespace std;
class GradedActivity
{protected:  double score;   // To hold the numeric score
 public:
    GradedActivity()
    {   score = 0.0;
        count << "\t GradedActivity, Default Constructor, score = "<<score<<"\n\n";
    }
    GradedActivity(double s)
    {   score = s;
        count << "\t GradedActivity, Constructor1, score = " << score << "\n\n";        
    }
    void setScore(double s)
    {   score = s;
        count << "\t GradedActivity setscore, score = " << score << "\n\n";       
    }
    double getScore() const
    {   return score;
        count << "\t GradedActivity getscore, score= " << score << "\n\n";        
    }
    char getLetterGrade() const
    {   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;
      count<<"\t PassFailActivity Default Constructor, minPassingScore= "<<minPassingScore<<"\n\n";     
    }
    PassFailActivity(double mps) : GradedActivity()
    { minPassingScore = mps;
      count << "\t PassFailActivity Constructor1, minPassingScore= "<<minPassingScore<<"\n\n";      
    }
    void setMinPassingScore(double mps)
    { count << "\t PassFailActivity setMinPassingScore, minPassingScroe= "<<minPassingScore<<"\n\n";
      minPassingScore = mps;
    }
    double getMinPassingScore() const
    {   count<<"\t PassFailActivity getMinPassingScore: "<<minPassingScore<<"\n\n";
        return minPassingScore;
    }
    char getLetterGrade() const
    {   char letterGrade;
        if (score >= minPassingScore) letterGrade = 'P';
        else letterGrade = 'F';
        count<<"\t PassFailActivity getLetterGrade, letterGrade= "<<letterGrade<<"\n\n";
        return letterGrade;
    }
};
class PassFailExam : public PassFailActivity
{
private: int numQuestions; double pointsEach; int numMissed;
public:  PassFailExam() : PassFailActivity()
    {  count << "\t PassFailExam Default Constructor: \n\n";
       numQuestions = 0;
       pointsEach = 0.0;
       numMissed = 0;
    }
    PassFailExam(int questions, int missed, double mps) :PassFailActivity(mps)
    { count << "\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);
      count << "\t PassFailExam set, numQuestion= "<<numQuestions<<
          ", numMissed= "<<numMissed<<", pointsEach= "<<pointsEach<<"\n\n";
      setScore(numericScore);
    }
    double getNumQuestions() const
    { count << "\t PassFailExam  getNumQuestions: " << numQuestions << "\n\n";
      return numQuestions;
    }
    double getPointsEach() const
    { count << "\t PassFailExam  getPointsEach: " << pointsEach << "\n\n";
      return pointsEach;
    }
    int getNumMissed() const
    { count << "\t PassFailExam  getNumMissed: " << numMissed << "\n\n";
      return numMissed;
    }
};
int main()
{   int questions;  int missed;  double minPassing;
    count << "How many questions are on the exam? "; cin >> questions;
    count << "How many questions did the student miss? "; cin >> missed;
    count << "Enter the minimum passing score for this test: "; cin >> minPassing;
    count << "\n\n";
    PassFailExam exam(questions, missed, minPassing);//What is this?
    count << fixed << setprecision(1) << "\nEach question counts "
        << exam.getPointsEach() << " points.\n The minimum passing score is "
        << exam.getMinPassingScore() << "\n The student's exam score is " <<
        exam.getScore() << "\n The student's grade is " << exam.getLetterGrade() << "\n\n";
    return 0;
}
this is the steps:
How many questions are on the exam? 45
How many questions did the student miss? 9
Enter the minimum passing score for this test: 60

1) GradedActivity, Default Constructor, score = 0

2) PassFailActivity Constructor1, minPassingScore= 60

3) PassFailExam Constructor 3:

4) PassFailExam set, numQuestion= 45, numMissed= 9, pointsEach= 2.22222

5) GradedActivity setscore, score = 80

6) PassFailActivity getLetterGrade, letterGrade= P

7) PassFailActivity getMinPassingScore: 60
 
  • #16
yungman said:
I am absolutely surprised that in line101 to 105 , it actually executing BACKWARDS

The order of evaluation of functions in an expression is are not determined by the language specification, meaning compilers can choose to evaluate function in a different order than implied by operator associativity.
See https://en.cppreference.com/w/cpp/language/eval_order for a good short description.

((I deleted my first answer to this where I, without thinking, wrongly went on to explain your observations as a result of operator associativity. If you somehow read that then please ignore that answer)).
 
  • Like
Likes   Reactions: yungman
  • #17
Filip Larsen said:
The order of evaluation of functions in an expression is are not determined by the language specification, meaning compilers can choose to evaluate function in a different order than implied by operator associativity.

While I know why they did thus, to my mind, this was a mistake:

C:
i=0;
x = foo(++i,++i,=+i);

Does it call foo(1,2,3)? foo(3,2,1)? Perhaps foo(2,1,3)? Or maybe...

And if that isn't scary enough, think about what this might return:

C:
y = bar(bar(x,i),--i);

If, of course, it returns anything at all.
 
  • Like
Likes   Reactions: yungman
  • #18
Vanadium 50 said:
While I know why they did thus, to my mind, this was a mistake:

C:
i=0;
x = foo(++i,++i,=+i);

Does it call foo(1,2,3)? foo(3,2,1)? Perhaps foo(2,1,3)? Or maybe...

And if that isn't scary enough, think about what this might return:

C:
y = bar(bar(x,i),--i);

If, of course, it returns anything at all.
Ha ha, don't write it like that!

I wonder if all the different IDEs give you different answers.That's why I always try not to get too fancy and just use simple lines even if I have to have a few more lines. When I started this thread, I was really a little freak out seeing something I thought I never saw before and there's no explanation in that section...Just to find it's covered in two chapters ago, just simple constructor with 3 arguments and I have good example in my notes. Too many little things, too many options. Unless you work with them everyday, how can you remember all of them?

I rather write a few get() and set() statements and forget all the fancy overloading constructors.

Of cause, being old really doesn't help.
 
Last edited:

Similar threads

  • · Replies 36 ·
2
Replies
36
Views
3K
  • · Replies 23 ·
Replies
23
Views
2K
  • · Replies 35 ·
2
Replies
35
Views
4K
  • · Replies 1 ·
Replies
1
Views
3K
  • · Replies 3 ·
Replies
3
Views
4K
  • · Replies 2 ·
Replies
2
Views
2K
  • · Replies 9 ·
Replies
9
Views
4K
  • · Replies 9 ·
Replies
9
Views
15K
  • · Replies 4 ·
Replies
4
Views
2K
  • · Replies 7 ·
Replies
7
Views
2K