C/C++ Understanding Inheritance and Polymorphism in C++

  • Thread starter Thread starter yungman
  • Start date Start date
  • Tags Tags
    Line
AI Thread Summary
The line "PassFailExam exam(questions, missed, minPassing);" in the C++ code is a constructor call that initializes an object of the PassFailExam class with three parameters. This syntax allows for the direct initialization of the object, which is different from declaring the object and then calling a constructor separately. The discussion clarifies that this line does not represent a function call but rather a constructor invocation, and the confusion arises from misunderstanding how object initialization works in C++. Additionally, the conversation touches on the importance of understanding class constructors and the implications of calling them correctly. Overall, the participants emphasize the need for practice to fully grasp these concepts in C++.
yungman
Messages
5,741
Reaction score
294
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
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 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 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 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 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 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 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:

Similar threads

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