0

What is wrong with the following code with respect to inheritance? Getting no matching function for call to ‘student::student()’ error.

class student {
 private:
  string firstname;
  string lastname;

 public:
  student(string fname, string lname) {
    firstname = fname;
    lastname = lname;
  }
  string getname() { return firstname + lastname; }
  void setfirstname(string fname) { this->firstname = fname; }
};

class undergraduate : public student {
 private:
  double gpa;
  student* stu;

 public:
  undergraduate(string firstname, string lastname, double gpa) {
    stu = new student(firstname, lastname);
    this->gpa = gpa;
  }
};

int main() {
  undergraduate stu1("Sam", "Singh", 4.0);
  stu1.setfirstname("temp");
  cout << stu1.getname();
}

Please point out the mistake and help me in rectifying it. Thanks!

JaMiT
  • 14,422
  • 4
  • 15
  • 31
taurus05
  • 2,491
  • 15
  • 28
  • 2
    You should call the student ctor like: `undergraduate(string firstname, string lastname, double gpa) : student(firstname, lastname), gpa(gpa) { }` and lose the `student* stu;` Seems like you are not understanding inheritance. – 001 Sep 10 '21 at 12:27
  • Base class constructors are always called in the derived class constructors. Whenever you create derived class object. Since you've no default ctor, Johnny's comment addresses how to call your ctor with 2 args. – Ch3steR Sep 10 '21 at 12:28
  • 1
    @JohnnyMopp I'm learning. – taurus05 Sep 10 '21 at 12:28
  • 1
    I didn't mean that as an insult. But maybe you should go back and re-read the chapter on inheritance. See also: [What are the rules for calling the base class constructor?](https://stackoverflow.com/q/120876) – 001 Sep 10 '21 at 12:30
  • 1
    @JohnnyMopp I never felt insulted. I was only saying that i'm learning and a detailed answer will help me more than just shortcut! – taurus05 Sep 10 '21 at 12:33
  • Maybe I'm wrong, but in your case there is no need for the `student* stu` member variable. An `undergraduate` **IS A** `student`, that's how inheritance works. Unless you need to have a reference to some other student instance, the `stu` member variable is not needed and likely is what lead to your error – Gian Paolo Sep 10 '21 at 12:37
  • @GianPaolo, I believe you're telling what i'm excatly looking for. if it was *HAS A* relationship, what should be the necessary changes? – taurus05 Sep 10 '21 at 12:38
  • @taurus05: In one of your previous comments, you wrote: `"i'm learning and a detailed answer will help me more than just shortcut!"` -- If you are asking for an answer that is essentially an entire tutorial on inheritance, then I'm afraid that is too much to ask for. Stack Overflow is not intended to provide personal tutorials on the basics of certain programming concepts. That is what [books](https://stackoverflow.com/q/388242/12149471) are for. Stack Overflow is rather intended as a place for asking specific questions, when something specific is unclear (after learning the basics in a book). – Andreas Wenzel Sep 10 '21 at 12:46
  • @taurus05: In your last comment, you wrote: `"if it was HAS A relationship, what should be the necessary changes?"` -- That depends on the type of relationship. Does one object own the other, so that when the owner gets destroyed, all owned objects should automatically get destroyed too? In that case, a [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr) would probably be appropriate. A raw pointer, as you have, would only be appropriate if it is guaranteed that the lifetime of the referenced object is identical to or exceeds that of the referencing object. – Andreas Wenzel Sep 10 '21 at 12:59
  • @taurus05: If there is no ownership relationship, but you still want to enforce that the referenced object does not get destroyed while it is still being referenced, then you may want to take a look at [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr). – Andreas Wenzel Sep 10 '21 at 13:06
  • @taurus05, the code you are showing is show exactly a relation **HAS A**, which is not what you need in this case. – Gian Paolo Sep 10 '21 at 17:09

2 Answers2

3

It looks like you might be a Python programmer, so here is your code, re-written in that langage

class student:
    def __init__(self, fname, lname):
        self.firstname = fname;
        self.lastname = lname;

    @property
    def name(self):
        return self.firstname + self.lastname

class undergraduate(student):
    def __init__(self, fname, lname, gpa):
        super().__init__(fname, lname)
        self.gpa = gpa
 
stu1 = undergraduate("Sam", "Singh", 4.0);
stu1.firstname = "temp";
print(stu1.name)

The first thing to notice is that the undergraduate does not contain a student member. Since it inherits from student (is a) there is no need for a member in undergraduate. It's the same for the C++ class.

However, in the Python code, the undergraduate class calls the student ctor in the body of the undergraduate ctor. That's different from how it works in C++. That language uses "initializer lists". These are used to not only call parent ctors but also initialize member variables.

class student {
 private:   // You may want to change to protected so child classes can access
  string firstname;
  string lastname;

 public:
  student(string fname, string lname) {
    firstname = fname;
    lastname = lname;
  }
  string getname() { return firstname + lastname; }
  void setfirstname(string fname) { this->firstname = fname; }
};

class undergraduate : public student {
 private:
  double gpa;
 
 public:
  undergraduate(string firstname, string lastname, double gpa) :
    student(firstname, lastname),  // Call parent ctor
    gpa(gpa)                       // Initialize this->gpa to gpa parameter
  {
      // The initializer list has taken care of everything so there's nothing here
      // But additional code could be added if needed.
  }
};

int main() {
  undergraduate stu1("Sam", "Singh", 4.0);
  stu1.setfirstname("temp");
  cout << stu1.getname();
}

If you are confused about how inheritance works, I strongly recommend you find a good book or tutorial. Answers on SO cannot go into enough detail to fully explain the concepts.
The Definitive C++ Book Guide and List
Constructors and member initializer lists


RE the original error in the code: the compiler is looking for a default ctor (no parameters) for student but you do not provide one. See: When do we need to have a default constructor?

001
  • 13,291
  • 5
  • 35
  • 66
  • OP never asked for a python code translation; hence the part of answer is redundant. – Const Sep 10 '21 at 13:30
  • 2
    @Const I think you mean "unnecessary" and not "redundant". But I disagree: it is there to show the concept in a form that the OP might be familiar with. – 001 Sep 10 '21 at 13:34
  • @Const, I am a python programmer. Hence, explaining my doubt by relating it to the way i'm thinking is a mystic art which i greatly adore. Nothing is redundant in the answer. – taurus05 Sep 10 '21 at 13:35
2

The undergraduate is a student. Therefore, the construction of undergraduate needs to first construct the parent class student.

Since you have not provided, how to construct the parent student in

 undergraduate(string firstname, string lastname, double gpa) 
 {
    stu = new student(firstname, lastname);
    this->gpa = gpa;
 }

before it comes to the constructor body, the compiler will try to do it for you by calling the default constructor of student. Since the student has parameterized constructor student(string fname, string lname), the compiler will not generate one for you and end up not having a default constructor for student. This leads to the error.

Secondly, you do not require the pointer to the parent, unless you have any other special requirements.

Therefore, you have two different options to solve the issue.

  1. Provide the default constructor for student.
  2. Or initialize the student in the child constructor (undergraduate), removing the redundant pointer to student.

Also use the constructor member initializer list for initializing the member of the class:

undergraduate(string firstname, string lastname, double gpa)
 :  student{ std::move(firstname), std::move(lastname)}
 ,  gpa{ gpa} 
 {}
Const
  • 1,306
  • 1
  • 10
  • 26
  • 1
    That `gpa{ gpa}` in the initializer list could use a bit of explanation. It's not obvious to beginners what it actually does. – Pete Becker Sep 10 '21 at 14:55