0

I am trying to initialize 2 dynamic arrays which class student will have an array containing all the courses registered and class course will have an array containing all the students registered to the class. I defined class Course and Student like this:

#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;

class Course;

class Student {

    std::string name;   // person’s name
    int id;            // person’s age
    Course* courses;
    int course_register;  // number of organization registered to 

public:


    Student();

    Student(const Student& s) {
        name = s.getName();
        id = s.getAge();
        course_register = s.getorg_register();

    }

    Student(std::string n, int i) {
        id = i;
        name = n;
    }

    int getAge() const { return id; }
    string getName() const { return name; }
    int getorg_register() const { return course_register; }


};

class Course {

    std::string name; // name of the org
    Student* students; // list of members
    int size;  // actual size of the org
    int dim;  // max size of the org


public:

    Course()
    {
        dim = 100;
        students = new Student[dim];
    };


    Course(const Course& course)
    {
        name = course.getName();
        dim = course.getDim();
        size = course.getSize();
        students = new Student[dim];
        for (int i = 0; i < size; i++) {
            students[i] = course.getStudent(i);
        }

    }

    ~Course()
    {
        delete[] students;
    }


    Student getStudent(int i) {return students[i];}
    Student* getStudents() {return students;}
    int getSize(){return size;}
    int getDim() {return dim;}
    string getName() {return name;}
};

Student::Student() {
    courses = new Course[5];
}

When I try to compile, I get an exception unhandled at runtime for constructor Student::Student(). Can someone explain to me why I get a runtime error? And how would you change it to make it work?

2 Answers 2

1

A Student should contain a list that refers to the Courses it is enrolled in, it should not create the Courses themselves.

A Course should contain a list that refers to the Students enrolled in it, it should not create the Students themselves.

Does that make sense? In this case, refers means "use a pointer". For instance, Student could have a std::vector<Course*>, and Course could have a std::vector<Student*>. Then you can have a method that enrolls a Student into a Course, where a pointer to the Student is added to the Course's list, and a pointer to the Course is added to the Student's list, eg:

Student.h

#ifndef StudentH
#define StudentH

#include <string>
#include <vector>

class Course;

class Student {

    std::string name;
    int id;
    std::vector<Course*> courses;

    void addCourse(Course *c);
    void removeCourse(Course *c);

    friend class Course;

public:

    Student(std::string n, int i);
    Student(const Student& s);
    Student(Student&& s);

    ~Student();

    Student& operator=(Student rhs);

    int getId() const;
    string getName() const;
    std::vector<Course*> getCourses() const;

    void enroll(Course &c);
    void drop(Course &c);
};

#endif

Student.cpp

#include "Student.h"
#include "Course.h"

Student::Student(std::string n, int i) {
    name = n;
    id = i;
}

Student::Student(const Student& s) {
    name = s.name;
    id = s.id;
    for (Course *c : s.courses) {
        c->enroll(*this);
    }
}

Student::Student(Student&& s) {
    name = std::move(s.name);
    id = s.id; s.id = 0;
    courses = std::move(s.courses);
    for (Course *c : courses) {
        c->removeStudent(&s);
        c->addStudent(this);
    }
}

Student::~Student() {
    for(Course *c : courses) {
        c->removeStudent(this);
    }
}

Student& Student::operator=(Student rhs) {
    Student temp(std::move(rhs));
    for (Course *c : courses) {
        c->removeStudent(this);
    }
    name = std::move(temp.name);
    id = temp.id; temp.id = 0;
    courses = std::move(temp.courses);
    for (Course *c : courses) {
        c->removeStudent(&temp);
        c->addStudent(this);
    }
    return *this;
}

void Student::addCourse(Course *c) {
    if (std::find(courses.begin(), courses.end(), c) == courses.end()) {
        courses.push_back(c);
    }
}

void Student::removeCourse(Course *c) {
    auto iter = std::find(courses.begin(), courses.end(), c);
    if (iter != courses.end())
        courses.erase(iter);
    }
}

int Student::getId() const {
    return id;
}

string Student::getName() const {
    return name;
}

std::vector<Course*> Student::getCourses() const {
    return courses;
}

void Student::enroll(Course &c) {
    c.enroll(*this);
}

void Student::drop(Course &c) {
    c.drop(*this);
}

Course.h

#ifndef CourseH
#define CourseH

#include <string>
#include <vector>

class Student;

class Course {

    std::string name;
    std::vector<Student*> students;

    void addStudent(Student *s);
    void removeStudent(Student *s);

    friend class Student;

public:

    Course(std::string n);
    Course(const Course& c);
    Course(Course&& c);

    ~Course();

    Course& operator=(Course rhs);

    string getName() const;
    std::vector<Student*> getStudents() const;

    void enroll(Student &s);
    void drop(Student &s);
};

#endif

Course.cpp

#include "Course.h"
#include "Student.h"

Course::Course(std::string n) {
    name = n;
}

Course::Course(const Course& c) {
    name = c.name;
    for (Student *s : c.students) {
        enroll(*s);
    }
}

Course::Course(Course&& c) {
    name = std::move(c.name);
    students = std::move(c.students);
    for (Student *s : students) {
        s->removeCourse(&c);
        s->addCourse(this);
    }
}

Course::~Course()
{
    for(Student *s : students) {
        s->removeCourse(this);
    }
}

Course& Course::operator=(Course rhs)
{
    Course temp(std::move(rhs));
    for (Student *s : students) {
        s->removeCourse(this);
    }
    name = std::move(temp.name);
    students = std::move(temp.students);
    for (Student *s : students) {
        s->removeCourse(&temp);
        s->addCourse(this);
    }
    return *this;
}

void Course::addStudent(Student *s) {
    if (std::find(students.begin(), students.end(), s) == students.end()) {
        students.push_back(s);
    }
}

void Course::removeStudent(Student *s) {
    auto iter = std::find(students.begin(), students.end(), s);
    if (iter != students.end())
        students.erase(iter);
    }
}

string Course::getName() const {
    return name;
}

std::vector<Student*> Course::getStudents() const {
    return students;
}

void Course::enroll(Student &s) {
    addStudent(&s);
    s.addCourse(this);
}

void Course::drop(Student &s) {
    removeStudent(&s);
    s.removeCourse(this);
}

Main.cpp

#include "Student.h"
#include "Course.h"

int main()
{
    Course c("Math");
    Student p("Joe", 12345); 

    p.enroll(c);

    return 0;
}
Sign up to request clarification or add additional context in comments.

Comments

1

You have infinite recursion. The Course constructor calls the Student constructor. The Student constructor calls the Course constructor. This continues until you've used up all the stack space.

You'll want to rethink your design of your two classes.

3 Comments

Any clues how to rethink the design?
You need to break the chain. Often decoupling by inserting an extra layer of abstraction helps. Just read that statement over. Wonder what that all means in English.
@George As written right now, every new Course has 100 new Students (unrelated to those in any other Course) and every new Student has 5 new Courses (unrelated to the Courses of any other Student). Putting it in those words, does that sound correct for what you're trying to achieve? Perhaps Students shouldn't create new Courses nor should Courses create new Students, but instead there should be a fixed amount of Students and Courses which then get associated together in whatever way that should be determined.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.