Skip to Content
Course content

207: Practice Exercise: Building a Student Grading System

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Alright, we've talked about the theory of data structures and object management, but it's time to actually get our hands dirty. We're going to build a Student Grading System. Now, I could just give you the finished code, but that's not how we actually work in the field. Let's build this out in real-time, make a few mistakes, and figure out the best way to handle the data.

Getting the skeleton together

First, I need a way to represent a student. A struct feels right here because we're mostly just grouping data together—a name and a grade. I'll throw them into a std::vector because I don't know how many students the user will enter.

struct Student {
    std::string name;
    double grade;
};

// ... inside main ...
std::vector<Student> classroom;
classroom.push_back({"Alice", 92.5});
classroom.push_back({"Bob", 78.0});
classroom.push_back({"Charlie", 88.2});

This looks clean enough. I can iterate through the vector and print the names and grades. So far, so good. But a grading system isn't useful if you can't modify the data, like when a student does extra credit.

Wait, why isn't the grade updating?

I want to write a quick helper function to bump up a student's grade. I'll try something like this:

void applyExtraCredit(Student s, double bonus) {
    s.grade += bonus;
}

// Calling it in main:
applyExtraCredit(classroom[0], 2.0); 
// Alice should now have 94.5

I run it, print Alice's grade, and... it's still 92.5. I've done this a hundred times, and it's always the same culprit. I'm passing the Student object by value. The function is creating a local copy of Alice, adding the bonus to that copy, and then destroying the copy when the function ends. The original Alice in my vector remains untouched.

I need to pass by reference so I'm operating on the actual memory location inside the vector:

void applyExtraCredit(Student& s, double bonus) {
    s.grade += bonus;
}

Now, when I run it, Alice's grade actually hits 94.5. It's a small change—just one & symbol—but it's the difference between a working program and a very frustrating hour of debugging.

Handling the "Not Found" problem

Now let's get more realistic. I want to find a student by name and update their grade. I'll loop through the vector and check the name. But what happens if the user typos "Alise" instead of "Alice"?

void updateGrade(std::vector<Student>& students, std::string targetName, double newGrade) {
    for (auto& s : students) {
        if (s.name == targetName) {
            s.grade = newGrade;
        }
    }
}

This works, but it's "silent." If the name isn't found, the function just finishes and the user has no idea why the grade didn't change. In a real app, that's a terrible user experience. I should probably return a boolean to indicate if the operation actually succeeded.

bool updateGrade(std::vector<Student>& students, std::string targetName, double newGrade) {
    for (auto& s : students) {
        if (s.name == targetName) {
            s.grade = newGrade;
            return true; // Found and updated!
        }
    }
    return false; // We searched everyone and found nothing.
}

By returning a bool, I can now wrap the call in an if statement in main and print a "Student not found" warning. It makes the program feel robust rather than fragile.

Calculating the class average

Last thing: I need the class average. I could just loop through and sum the grades, but since we're using C++, I'm tempted to use <numeric>. However, std::accumulate doesn't natively know how to handle a vector of Student structs—it wants a vector of doubles.

I could write a manual loop, which is fine, but it's a great excuse to use a lambda. I'll tell accumulate to start at 0.0 and, for every student it encounters, just add their grade member to the running total.

#include <numeric>

double total = std::accumulate(classroom.begin(), classroom.end(), 0.0, 
    [](double sum, const Student& s) {
        return sum + s.grade;
    });

double average = total / classroom.size();

There it is. We started with a simple list, hit a wall with pass-by-value, fixed our error handling for searches, and used a lambda to keep the math concise. That's a basic but functional grading system.




📋 Practical Task

Exercise: Implement a Grade Distribution Report

Using the Student Grading System logic from the lesson, extend the program to generate a "Grade Distribution Report." Your task is to write a function that analyzes the std::vector<Student> and counts how many students fall into specific grade brackets.

Requirements:

  • Create a function void printDistribution(const std::vector<Student>& students).
  • The function should count and print how many students are in the following categories:
    • A: 90 and above
    • B: 80 to 89.9
    • C: 70 to 79.9
    • D: 60 to 69.9
    • F: Below 60
  • The function must take the vector by const reference to ensure no student data is accidentally modified during the report generation.
  • Test your function with a vector containing at least 5 students with varying grades.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.