0

Trying to build a database using classes.

This is just an excerpt of the classes, my main() creates a bunch of students using the class Student. Each student then has an ID and Name that are inputted later. Additionally, each student will have an array of 2 slots which will hold info for their courses they're taking. Those courses are created using the class Course.

What I'm trying to figure out is how can I place the course info (courseID and courseName) into a slot of the student's courses array once I assign them a course (in other words, student A is now in class 1. I want the courseID and courseName of class 1 to be assigned to student A's courses).

I try to use the locations of each course created in the main but that proves difficult trying to output. Is it possible to be in the class Student and have it call a function from class Course? Any help be great. Thanks.

class Course {
protected:
    int courseID;
    char* courseName;
public:
    Course() {
        courseID = 0;
        courseName = "";
    }

    void makeID(int id, char* name) {
        courseID = id;
        courseName = name;
    }

    int getID() {
        return courseID;
    }

    char* getCourseName() {
        return courseName;
    }


};

class Student : public Course {
private:
    int studentID;
    char* studentName;
    int classCount;
    int courses[2]; //could I change to: Course courses[2]?
    char name[30];
    int id;

public:
    Student() {
        studentID = 0;
        studentName[30];
        classCount = 0;
        courses[2];
    }

    void makeStudent() {
        cout << "Input 9 digit student ID: ";
        cin >> id;
        studentID = id;
        cin.ignore();
        cout << "Input first and last name of the student: ";
        cin.getline(name, 30, '\n');
        studentName = name;
        return;
    }

    int getstudentID() {
        return studentID;
    }

    int getclassCount() {
        return classCount;
    }

    char* getstudentName() {
        return studentName;
    }

    void addClass(int course) {
        if (classCount == 2) {
            cout << "Max amount of courses reached." << endl;
        }
        else {
            courses[classCount] = course;
            classCount++;
        }
        return;
    }

    int returnClass(int course) { //can't figure out what to do 
        return courses[course];   //here. 
    }

};
CD'A
  • 41
  • 5
  • 2
    Remember that inheritance is an "is-a" relation. Is `Student` really a `Course`? – Some programmer dude Jul 28 '18 at 15:45
  • It's unclear what exactly is preventing you from trying to do just that, declare `Course courses[2];`. It'll probably be faster to try it, and see what happens, then to wait for someone else's blessings. – Sam Varshavchik Jul 28 '18 at 15:45
  • And the statement `courses[2];` by itself does nothing except index out of bounds, leading to [*undefined behavior*](https://en.wikipedia.org/wiki/Undefined_behavior). Perhaps you should [get a couple of good books](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list/388282#388282) and start reading from the beginning? – Some programmer dude Jul 28 '18 at 15:46

1 Answers1

0

At very first, you should get a clear image how your data model shall look like.

OK, we have bunch of courses and a bunch of students. Courses and students are totally unrelated (apart from students attending courses) concepts at first, so it won't make any sense one inheriting from the other...

Imagining the scenario does not only cover one single period (school year, semester, trimester, ...), courses might change over time as well as will the students.

So you might have a vector or another data structure storing courses on one hand and students on the other.

Students will attend courses, this can be reflected in two ways:

  • courses holding lists of all students attending them
  • students holding lists of all courses they attend

Depending on use case, it might be appropriate to implement both redundantly.

How would you install such a relation ship? Easiest (at a first glance, at least) would be X holding one or more pointers to Y in both scenarios.

class Course
{
    std::vector<Student*> participants;
public:
    // whatever you need...
};

class Student
{
    std::vector<Course*> students;
public:
    // whatever you need...
};

As I now use pointers to represent the relation ship, the data structures holding all courses and students must not invalidate these if adding or removing students from! std::vector<X> would do so, so you cannot use it. Alternatives would be std::vector<std::unique_ptr<X>> or std::list<X>. Actually, std::vector<X*> would work as well, but then it would be you who would need to care for deletion, if removing courses or students, so prefer one of the other solutions.

But why did I not use smart pointers for the courses' and students' vectors? We'd be forced to use std::shared_ptr then for, but we won't profit from: If a student leaves our institution, we'd have to eliminate her/him anyway and remove her/him from all courses being attended. So we don't profit from smart pointers in this scenario (can be totally different in other ones). Analogously if a course is cancelled.

So make sure in the destructors the classes that

  • a course is removed from all students' vectors in its own vector
  • student is unregistered from all courses in its vector

With these basics, you can get ID, name and other data for a specific course a student attends simply via the pointer in the vector.

If you often seek via a specific information, (e. g. by name), you might have a std::map as well, facilitating the lookup by the specific attribute. You might use such a map for the complete list of courses/students as well, then, though, be aware that pointers are only guaranteed to remain valid in std::map<X, Y>, not in std::unordered_map<X, Y>, so if you intend to use the latter, you'd have to go the way via smart pointer again as with the vector: std::unordered_map<X, std::unique_ptr<Y>>.

Aconcagua
  • 24,880
  • 4
  • 34
  • 59