C++ PROGRAMMING ยท 40 MIN STUDY

Project: Student record manager with files and vectors

Build a menu-driven student record manager using structs, vectors, validation, searching, and file persistence.

Learning objectives

  • Design a student record type and store records in a vector.
  • Implement add, list, search, update, and delete operations with validation.
  • Explain why pointers or iterators into a vector may become invalid after modification.
  • Define a file format and test malformed, missing, and duplicate records.

Prerequisites: structs, vectors, functions, loops, file streams, and basic exception handling. This page is a guided project specification with focused code fragments; for a complete buildable multi-file example, follow the library catalogue capstone.

Project goal

Project scope: this page is a guided implementation brief with selected code fragments, not a complete source listing. A complete multi-file build-and-test example is available in the library catalogue capstone.

Build a console application that stores student records, lists them, searches by ID, adds new records, and saves data to a text file. The project practices structs, vectors, functions, loops, validation, and file streams.

Data model

#include <cmath>
#include <string>

struct Student {
    int id;
    std::string name;
    double marks;
};

bool valid_student(const Student& student) {
    return student.id > 0 &&
           !student.name.empty() &&
           student.name.find_first_of("|\r\n") == std::string::npos &&
           std::isfinite(student.marks) &&
           student.marks >= 0.0 &&
           student.marks <= 100.0;
}

Keep the record simple. Require a positive unique ID and finite marks in the documented range (0 to 100 in this example). Because the teaching file format uses | between fields and one record per line, reject names containing |, carriage returns, or newlines. The valid_student helper demonstrates these checks; call it before adding or updating a record and again after parsing a saved record.

Find a student

#include <algorithm>
#include <vector>
Student* find_student(std::vector<Student>& students, int id) {
    auto it = std::find_if(students.begin(), students.end(),
        [id](const Student& s) { return s.id == id; });
    return it == students.end() ? nullptr : &*it;
}

This linear search takes O(n) time. Returning a pointer to a vector element is safe only while the element remains alive and the vector does not perform an invalidating reallocation or erase. Do not keep the pointer across operations that may modify the vector; a stable ID is often a better long-term handle.

Save records

#include <fstream>
#include <stdexcept>
#include <vector>

void save(const std::vector<Student>& students) {
    // Validate everything before opening the file in truncation mode.
    for (const auto& student : students) {
        if (!valid_student(student)) {
            throw std::runtime_error("Refusing to save an invalid student record");
        }
    }

    std::ofstream out("students.txt");
    if (!out) throw std::runtime_error("Cannot open students.txt");

    for (const auto& student : students) {
        out << student.id << '|'
            << student.name << '|'
            << student.marks << '\n';
    }
    out.flush();
    if (!out) throw std::runtime_error("Write or flush to students.txt failed");
    out.close();
    if (out.fail()) throw std::runtime_error("Closing students.txt failed");
}

This simple delimiter format assumes names do not contain the delimiter or newline; valid_student enforces that restriction. A production format needs escaping or a structured format such as correctly quoted CSV or JSON. This function checks open, write, flush, and close failures, but it writes directly to the destination file: a crash or storage failure during the write can still leave a partial file. A more robust design writes a temporary file, verifies it, and then replaces the destination using the platform's file-replacement guarantees.

Required features

  1. Load existing records when the program starts.
  2. Display all records in a readable table.
  3. Add a student only if ID is unique and marks are valid.
  4. Search by ID and show a clear not-found message.
  5. Update marks and delete records safely.
  6. Save after successful changes and handle file errors.

Test plan

  • Empty file and missing file.
  • Duplicate ID and invalid marks.
  • Searching for an existing and missing ID.
  • Names with spaces.
  • Malformed input line and failed save.

Extensions

Replace the linear search with an unordered_map keyed by ID, but explain the trade-off and keep one authoritative data store. Add sorting by marks, report averages, and separate the program into header and source files with CMake.

Completion checklist

  • All input is validated and the program never silently accepts malformed records.
  • Data survives program restart.
  • Functions have focused responsibilities.
  • Normal and edge cases are tested.

References