Skip to content

Practice Lab 18 — University course registration

Difficulty: Intermediate · Estimated time: 5–6 hours · Module 9: Composition, Aggregation and Association

An aggregation-heavy model: the same student object appears in many places, and the question is whether anybody owns them.


At a glance

Difficulty Intermediate
Estimated time 5–6 hours
Module Module 9 — Composition, Aggregation and Association
Concepts practised Aggregation versus composition, shared objects, many-to-many relationships, dependency direction, deleting safely, const access to shared objects
Prerequisite Practice Lab 17
Class design yours — the relationships are the deliverable

Scenario

A department registers students for courses. A student enrols in several courses; a course holds many students. Instructors teach sections, and a section belongs to one course. Rooms are booked by sections.

The registrar's old system kept copies of each student inside each course, and after a fee change half the department had the old fee recorded. That is the failure this lab is designed to prevent: shared data that gets copied is shared data that gets inconsistent.


Problem statement

Design and implement the department's registration model.

  1. Students, courses, sections, instructors and rooms, with the relationships the department describes.
  2. Many-to-many enrolment: a student in several sections, a section with many students — with no copy of a student stored anywhere.
  3. A capacity limit per section, refused when exceeded.
  4. An enrolment type that records when a student joined a section and their current state.
  5. Reports: a student's timetable, a section's roster, a room's bookings for a week, and a course's total enrolment.
  6. A fee per student, computed once from their programme, and demonstrably the same value wherever it is read.
  7. Narration of construction and destruction in every class.

Deliverables in relationships.md:

  • A diagram of the model with the relationship kinds and multiplicities, including the many-to-many.
  • For each relationship: owned, shared, or merely referenced — and the code that says so.
  • The answer to this question: if a student leaves, what is destroyed, and what is only unlinked? with the test that proves it.

Learning objectives

By the end of this lab you will be able to:

  • Model a many-to-many relationship without copying either side.
  • Decide whether an object is owned, shared or referenced, and express it in the member's type.
  • Delete or unlink safely when an object leaves the system, and prove nothing dangles.
  • Read a value through several paths and show it is one value, not several copies.
  • Say what a capacity limit is a property of, and enforce it in the right place.

Functional requirements

  1. A student appears in every report with the same fee value, read through different paths — demonstrable by changing it once and printing every path.
  2. No class stores a copy of a student or a course; the copy narration proves it.
  3. Enrolment refuses: a full section, a duplicate enrolment, an unknown section, and a student already in another section of the same course.
  4. Dropping an enrolment unlinks it, and the student and section both survive.
  5. A student who leaves is removed from every roster, and the program prints the number of rosters changed.
  6. A section's room booking cannot overlap another section's in the same room — with the rule enforced in one place.
  7. The timetable is time-ordered and stable, with a documented tie-break.
  8. An instructor's load is reported, and an instructor over a documented limit is refused a new section.
  9. Room bookings for a week are printed in a documented order.
  10. A const department prints every report.

Constraints

  • Header and source files per class family, with a documented build command.
  • No smart pointers in this lab (Module 11 will convert it). Ownership is expressed by value membership, and sharing by reference — with every reference's owner documented.
  • No templates.
  • No friend, no inheritance except one honest is-a if you find one — and justify it if you use it.
  • Narration in every constructor, copy constructor and destructor.
  • No warnings.

Starter task

In relationships.md, before coding:

  1. Write the multiplicities for every pair of types: student↔section, section↔course, section↔room, instructor↔section.
  2. For the many-to-many, decide where the enrolment record lives, and what it holds: references to both sides, or indices, or something else. Give the cost of each.
  3. Predict the destruction order when the department is destroyed while enrolments exist.
  4. Answer: is a room owned by the department, shared among sections, or referenced by bookings? Write the member declaration that expresses your answer.

Implementation tasks

  1. Implement the value types (student, course, room, instructor) with narration.
  2. Implement the enrolment and the many-to-many, without copying either side.
  3. Implement the refusals, the dropping, and the student-leaves path.
  4. Implement the reports and the room-booking overlap rule.
  5. Write main with the test cases, and print the roster count after a student leaves.
  6. Compare the narration with your prediction, and record the differences.

Test cases

# Action Expected result
1 Enrol a student in a section Enrolment created; both sides show it
2 Enrol the same student again Refused
3 Enrol into a full section Refused, naming capacity and current count
4 Enrol the same student in two sections of one course Second refused
5 Print a student's timetable Time-ordered, with a documented tie-break
6 Print a section's roster Every enrolled student, once each
7 Change a student's fee, then print all reports The same new value everywhere — no stale copies
8 Drop an enrolment Unlinked; student and section both still narrate nothing on destruction
9 A student leaves Roster count changes for every section affected, printed
10 Book a room for an overlapping time Refused, naming the clash
11 Total enrolment for a course Equals the sum of its sections' rosters
12 Print a const department Compiles, same output
13 Copy narration during enrolment Zero copies of a student or a course

Boundary cases to run deliberately: a section with capacity 1; a student with no enrolments; a course with no sections; a room with one booking.


Expected behaviour

[dept ctor] Data Science
[student ctor] 2024003 Ayesha Khan   fee 120000
[section ctor] CS-201 section A  room R-1  capacity 2
[enrol ctor] 2024003 -> CS-201/A  at 20260414
enrol 2024004 -> CS-201/A -> accepted (1 of 2)
enrol 2024005 -> CS-201/A -> refused: full (2 of 2)
student fee through the department report: 120000
student fee through the section roster:     120000
fee changed to 150000 -> both reports now print 150000
student 2024003 leaves -> rosters changed: 1
no copies of a student were made: copy narration count 0

Extension challenge

Add waitlists per section and automatic promotion when somebody drops.

Record in relationships.md:

  • Where the waitlist lives, and what it holds — references, enrolments in a state, or something else. Give the cost of each option in one line.
  • What happens if a student on a waitlist drops out of the system entirely, and how your design finds out.
  • Which of your reports now needs a new line, and how many files that touched.
  • Whether the promotion rule belongs to the section or to the department, and why.

Debugging challenge

The reports disagree about a student's fee, and the model looks correct:

class Section {
public:
    void enrol(Student student) {          // by value, "so the section owns its students"
        students_.push_back(student);
    }
    int total_fees() const {
        int total = 0;
        for (const Student& student : students_) {
            total += student.fee();
        }
        return total;
    }
private:
    std::vector<Student> students_;
};

Reproduce it — enrol a student in two sections, change the fee, print both rosters — and answer:

  1. Why do the two reports disagree, and which copy is stale?
  2. Which relationship did the code create, and which one did the scenario need?
  3. What is the smallest change that fixes it, and what does it cost in lifetime risk (who must now outlive whom)?
  4. Which test case in the table catches it?

Reflection questions

  1. Where is the enrolment record stored, and does either side own it? Say what happens to it when one side disappears.
  2. Changing the fee once and seeing it in every report is the evidence that nothing is copied. Did your copy narration confirm it?
  3. Which reference in your model has the weakest lifetime guarantee, and what would you write in a comment to warn a reader?
  4. The room-booking rule is enforced in one place. Which class owns it, and what does it need to know about the other classes?
  5. Your many-to-many is a container on each side, or a container of enrolments, or both. What are the duplicate risks, and how did you handle them?
  6. If the department had two campuses with shared courses, which relationships would change, and which ones would stay exactly as they are? Answer with the multiplicities you would write.

Completion checklist

  • [ ] It builds from a documented command with -Wall -Wextra -Werror and no warnings
  • [ ] relationships.md has the diagram, multiplicities and the owned/shared/referenced decision per relationship
  • [ ] No student or course is copied into a container — the copy narration proves it
  • [ ] The fee read through different paths reports the same value after a change
  • [ ] Enrolment refuses all four invalid cases, each with its reason
  • [ ] Dropping unlinks, and neither side is destroyed
  • [ ] A leaving student updates every affected roster, with the count printed
  • [ ] The room-booking overlap is refused, with the rule in one place
  • [ ] All thirteen test cases run, with output recorded
  • [ ] The debugging challenge was explained before being fixed
  • [ ] I can state what is destroyed and what is only unlinked when any object leaves

Related: Lab index · Module 9 — Composition and Association · Previous: Practice Lab 17 · Next: Practice Lab 19 — Dynamic collection: Rule of Three to Rule of Five