0

guys, I have the following code, but I'm getting error at compiling time... the error is at the end. Thanks 1st Class is "Person"

#ifndef PERSON_H//protecting .h files
#define PERSON_H//protecting .h files
#include <iostream>
#include <string>
using namespace std;

class Person
{
public:
    Person();
    Person(string first, string last)
    {
        firstName = first;
        lastName = last;
    }
    virtual void setName(string first, string last)
    {
        firstName = first;
        lastName = last;
    }
    virtual void setWeightAge(int w, int a)
    {
        weight = w;
        age = a;
    }
    virtual string getFirstName()
    {
        return firstName;
    }
    virtual string getLastName()
    {
        return lastName;
    }
    virtual int getWeight()
    {
        return weight;
    }
    virtual int getAge()
    {
        return age;
    }
    virtual void printPerson()
    {
        cout << "Name: " << firstName << " " << lastName << endl;
        cout << "Age: " << age << endl;
        cout << "Weight: " << weight << endl;
    }
protected:
    string firstName, lastName;
    int weight, age;
};
#endif

2nd Class is "Student"

#ifndef STUDENT_H//protecting .h files
#define STUDENT_H//protecting .h files
#include <iostream>
#include <string>
#include "Person.h"
using namespace std;

class Student : public Person
{
public: 
    Student();
    Student(Person s)
    {
        sStudent = s;
    }
    virtual void setGPA(double g)
    {
        gpa = g;
    }
    virtual void setSchedule(string c, string t, string d)
    {
        stClass = c;
        time = time;
        days = d;
    }
    virtual void setGrade(char g)
    {
        grade = g;
    }
    virtual double getGPA()
    {
        if (grade == 'a') { gpa = 4.0; }
        if (grade == 'b') { gpa = 3.0; }
        if (grade == 'c') { gpa = 2.0; }
        else gpa = 0;
        return gpa;
    }
    virtual char getGrade()
    {
        return grade;
    }

    virtual void printSchedule()
    {
        cout << "Class | Days | Time " << endl;
        cout << stClass << " | " << days << " | " << time << endl;
    }
protected:
    string stClass, time, days;
    char grade;
    double gpa;
    Person sStudent;
};
#endif

and Main()

#include <iostream>
#include "Student.h"
using namespace std;

int main()
{
    //creating a person
    Person john("John", "Smith");
    john.setWeightAge(180, 39);
    john.printPerson();

    //making john a student
    Student johnStdntMath(john);
    johnStdntMath.setSchedule("Math", "7:45", "M, W");
    johnStdntMath.setGrade('b');
    johnStdntMath.printPerson();
    johnStdntMath.printSchedule();

    system("pause");
    return 0;

errors:

1>------ Build started: Project: Person, Configuration: Debug Win32 ------
1>  main.cpp
1>main.obj : error LNK2019: unresolved external symbol "public: __thiscall Person::Person(void)" (??0Person@@QAE@XZ) referenced in function "public: __thiscall Student::Student(class Person)" (??0Student@@QAE@VPerson@@@Z)
1>c:\users\jorge\documents\visual studio 2010\Projects\Person\Debug\Person.exe : fatal error LNK1120: 1 unresolved externals
}
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
miatech
  • 2,150
  • 8
  • 41
  • 78
  • Why have you made all of the functions virtual? Does it actually make sense to allow a derived class to override the implementation of `getFirstName` or `setName`? – James McNellis Feb 17 '11 at 02:31
  • Is this Visual Studio? What version is this? The error message is awful, it barely speaks proper C++. – wilhelmtell Feb 17 '11 at 02:33
  • @wilhelmtell: Maybe I'm too used to programming in C++, but I think the error is pretty good. It names the function for which it can't find a definition and tells you where the definition was needed. It even helpfully demangles the name for you. I'm not sure what other information the linker has available that it could give you. – James McNellis Feb 17 '11 at 02:41
  • @JamesMcNellis It's not missing information, on the contrary, it's perfectly alright on that front. But `Person::Person(void)`? `Student::Student(class Person)`? What sort of dialect is that? :p – wilhelmtell Feb 17 '11 at 02:43
  • @wilhelmtell: Well, the former is perfectly valid. In the latter, it is (I presume) attempting to be helpful by telling you that `Person` is a class type. I don't know for sure, I've never paid that much attention to this error. – James McNellis Feb 17 '11 at 02:47

4 Answers4

3

I suggest you double check your is-A and has-A relationships.

Writing Student : public Person says that a Student is-A Person. But later, you have a member variable sStudent of type Person, which says a Student has-A Person, and I'm guessing is not what you really want.

Check out the answers to this question: Inheritance vs. Aggregation for better explanations.

Community
  • 1
  • 1
Pablo
  • 8,644
  • 2
  • 39
  • 29
  • That's a good point and true. I'm going to suggest a variation on that in my answer. +1 – wilhelmtell Feb 17 '11 at 02:51
  • yeah, you're right no need to pass a Person object as a parameter to a student constructor... once I create the student object I can use setName function from Person class... but there are instances where an object is pass as a parameter or by reference... could you point out a case – miatech Feb 17 '11 at 07:25
1

Listen to your linker, it's just as it says: In constructor Student::Student(Person) you're referring to constructor Person::Person(), but you didn't define Person::Person(), not in a way the linker can see when it does its thing with the Student constructor.

Technically, because you are filling in sStudent in the Student constructor's body the compiler first default-initializes the Person object sStudent, and then assigns to it s, the Person parameter of the constructor. If you'd use the initializer list then the Person member wouldn't be default-initialized and then assigned to but rather copy-constructed right away:

Student(const Person& s) : sStudent(s) { }

But the question remains: Why are you publicly declaring a default constructor in Person and not define it?

Also, you have a leak in Student. The string and Person members of Student won't clean up because when a Student object destructs its destructor won't be called. The Person destructor will be called, but not the Student destructor, and the reason being that the destructor of Person is non-virtual.

One more thing: It's a bad idea in object-oriented design in general and C++ in particular to use inheritance for reuse. The reason is that this very often leads to a violation of the LSP. It can also bear a (not major but nonetheless) performance overhead for introducing a virtual table. But it's the correctness that suffers that matters when you pick inheritance when you should really be using delegation.

wilhelmtell
  • 57,473
  • 20
  • 96
  • 131
1

You are accessing the no argument constructor for Person when you create the johnStdntMath instance. You need to either

  1. Implement Person::Person() or ...
  2. Change Student::Student(Person s) to Student::Student(Person const& s)

There are some other problems in your code as well. Student is-a Person so there is no need for the Student class to have a Person member variable - it shares the instance variables of its base class by virtue of inheritance. In other words, Student extends Person so your program could be written:

int main() {
    Student johnStdntMath;
    johnStdntMath.setName("John", "Smith")
    johnStdntMath.setWeightAge(180, 39);
    johnStdntMath.setSchedule("Math", "7:45", "M, W");
    johnStdntMath.setGrade('b');
    johnStdntMath.printPerson();
    johnStdntMath.printSchedule();
    return 0;
}

I would also avoid the using namespace std; statement anywhere and especially in a header file. For example, your Student class contains a member variable named time. This will conflict with the std::time_t std::time(std::time_t*) function defined in <ctime> if that header is included before "student.h".

animuson
  • 53,861
  • 28
  • 137
  • 147
D.Shawley
  • 58,213
  • 10
  • 98
  • 113
0

You haven't implement the default constructor Person(),you can write it like this:

Person():weight(0)
        ,age(0){};

If you just want to complier it, this is enough;
Here are some tips below:
1.Check your mind, does class Student really need a Person member. If you really need it, Student(Person) may add an explicit symbol:

explicit Student(const Person& s) : sStudent(s) {...};  

2.In Person.h

  protected:
        string firstName, lastName;
        int weight, age;

the protected may be private?

Sid Zhang
  • 972
  • 3
  • 9
  • 18
  • I have done code before that the constructor does not initialize any variables... why in this case needed to initialize those variables? – miatech Feb 17 '11 at 07:32
  • Constructor is used for giving the member a proper initialization.For the constructor with params,the params usually is used for initializing the member:`Person(int w, int a):weight(w),age(a){}`. For the default Constructor, you need to initialize the member too: `Person():weight(0),age(0){}`.In this case, `firstName` and `lastName` is initialized to `""` while `weight` and `age` are `0`. You may ask why `firstName` is `""`? Because the `string` Class has default initialized it, but `int` type hasn't,so we give it a value `0`:`weight(0)`. Overall, give every member a proper value when coding! – Sid Zhang Feb 18 '11 at 01:48