Understanding Inheritance and Polymorphism in C++

  • C/C++
  • Thread starter yungman
  • Start date
  • Tags
    Line
In summary, the conversation discusses the use of constructors and objects in C++ and the confusion surrounding a specific line of code. The line in question calls two different constructors and may have potential complications if they modify a global counter. The conversation also touches on the difficulty of understanding unfamiliar programming paradigms.
  • #1
yungman
5,755
293
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;
    cout << "How many questions are on the exam? "; cin >> questions;
    cout << "How many questions did the student miss? ";cin >> missed;
    cout << "Enter the minimum passing score for this test: ";cin >> minPassing;
    PassFailExam exam(questions, missed, minPassing);//What is this?
    cout << 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
  • #3
jedishrfu said:
it’s calling a PassFailExam constructor with three parameters
which is defined on line 53.
 
  • Like
Likes Vanadium 50, Jarvis323, sysprog and 2 others
  • #4
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
 
  • #5
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 yungman, Jarvis323 and sysprog
  • #6
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 Vanadium 50 and yungman
  • #7
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:
  • #8
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 yungman
  • #9
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 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 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++:
    cout << 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;
        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";        
    }
    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;
      cout<<"\t PassFailActivity Default Constructor, minPassingScore= "<<minPassingScore<<"\n\n";     
    }
    PassFailActivity(double mps) : GradedActivity()
    { minPassingScore = mps;
      cout << "\t PassFailActivity Constructor1, minPassingScore= "<<minPassingScore<<"\n\n";      
    }
    void setMinPassingScore(double mps)
    { cout << "\t PassFailActivity setMinPassingScore, minPassingScroe= "<<minPassingScore<<"\n\n";
      minPassingScore = mps;
    }
    double getMinPassingScore() const
    {   cout<<"\t PassFailActivity getMinPassingScore: "<<minPassingScore<<"\n\n";
        return minPassingScore;
    }
    char getLetterGrade() const
    {   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;
    }
};
int main()
{   int questions;  int missed;  double minPassing;
    cout << "How many questions are on the exam? "; cin >> questions;
    cout << "How many questions did the student miss? "; cin >> missed;
    cout << "Enter the minimum passing score for this test: "; cin >> minPassing;
    cout << "\n\n";
    PassFailExam exam(questions, missed, minPassing);//What is this?
    cout << 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 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 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:

FAQ: Understanding Inheritance and Polymorphism in C++

1. What does this line mean?

This is a common question asked by many individuals when reading a scientific paper or research article. It could also refer to a specific line in a lab report or data analysis. In order to fully understand the meaning of a line, it is important to consider the context in which it is written. This includes the overall research question, methodology, and results. If you are still unsure, it is always best to consult with the author or a fellow scientist for clarification.

2. Why is this line important?

Many people may ask this question when they come across a confusing or seemingly insignificant line in a scientific document. However, it is important to remember that every line in a scientific paper serves a purpose and contributes to the overall understanding of the research. Even if a line may seem unimportant, it could provide crucial information that supports or challenges a hypothesis or existing theory.

3. Can you explain this line in simpler terms?

Sometimes, scientific language and terminology can be difficult for non-scientists to understand. If you come across a line that is particularly complex or jargon-heavy, it is perfectly acceptable to ask for a simpler explanation. As scientists, it is our responsibility to communicate our findings to a wider audience, so we should be able to explain our work in a way that is easily understandable to others.

4. How does this line relate to the research question?

In order to fully understand a line in a scientific document, it is important to consider how it relates to the overall research question or hypothesis. This will give you better insight into the purpose and significance of the line. If you are having trouble making this connection, it may be helpful to go back and review the research question and methodology to get a better understanding of the context.

5. Are there any limitations to this line?

It is common for scientific research to have limitations, and this can also apply to specific lines in a document. Some lines may be based on assumptions or may not be applicable in certain situations. It is important to acknowledge these limitations and understand how they may impact the interpretation of the line and the overall research findings.

Similar threads

Replies
36
Views
2K
Replies
23
Views
2K
Replies
35
Views
3K
Replies
5
Views
2K
Replies
1
Views
3K
Replies
4
Views
3K
Replies
89
Views
5K
Replies
1
Views
1K
Back
Top