c++17 – University Grading System (OOP)

I’ve been furthering my development and understanding in OOP (Object Oriented Programming) and would appreciate feedback on this new console program I’ve created. The main aspects I would like to know is have I made good use of OOP, can I improve upon techniques demonstrated in this program? Have I done anything bad? I do understand I should be loading data from a text-file, but that’s not the point of this program. For quick run of the program I’ve put sample data which is generated in the “University” constructor. I also understand that’s probably not the best name to call my class. I based this program off the following scenario. Many thanks to anyone who gives me any feedback!
enter image description here

// Progress of Students Calculator.cpp : This file contains the 'main' function. Program execution begins and ends there.
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <map>
class User 
{
    static int currentID;
    int m_ID;
    std::string m_Name;
public:
    User(std::string name) : m_Name{ name }, m_ID {currentID++}{
}
    int ID() const { return m_ID; }
    std::string name() const { return m_Name; }
};
int User::currentID = 1;
class Subject {
    static int currentID;
    int m_ID;
    std::string m_Name;
public:
    Subject(std::string name) :m_ID{ currentID++ }, m_Name{ name }{}
    int ID()const { return m_ID; }
    std::string name()const { return m_Name; }
};
int Subject::currentID = 1;
class Department {
    static int currentID;
    int m_ID;
    std::string m_Name;
public:
    Department( std::string name) :m_ID{ currentID++ }, m_Name{ name }{}
    int ID()const { return m_ID; }
    std::string name()const { return m_Name; }
};
int Department::currentID = 1;

class Lecturer : public User {
    int m_SubjectID;
    int m_DepartmentID;
public:
    Lecturer(std::string name, int subjectID, int departmentID) 
        :User(name), m_SubjectID(subjectID), m_DepartmentID(departmentID) {}
    int subjectID()const { return m_SubjectID; }
    int departmentID()const { return m_DepartmentID; }
};

class Student : public User {
    int m_ClassID;
    int m_Result;
public:
    Student(std::string name, int classID, int result)
        :User(name), m_ClassID(classID), m_Result(result) {}
    int classID() const { return m_ClassID; }
    int result() const { return m_Result; }
   
};

class Class 
{
    static int currentID;
    int m_ID;
    int m_LecturerID;
    int m_Size;
    int m_TotalScore;

public:
    Class(int lecturerID) 
        :m_ID{ currentID++ }, m_LecturerID{ lecturerID }, m_Size{ 0 }, m_TotalScore{0}
    {
    }
    int ID()const { return m_ID; }
    int lecturer_ID() const { return m_LecturerID; }
    int size() const { return m_Size; }
    void increment_size() { m_Size++; }
    int total_score() const { return m_TotalScore; }
    void total_score(int result) { m_TotalScore += result; }
};
int Class::currentID = 1;

    class University {
    private:
        std::vector<Department>objDepartments;
        std::vector<Subject>objSubjects;
        std::vector<Lecturer>objLecturers;
        std::vector<Student>objStudents;
        std::vector<Class>objClasses;

        auto find_lecturer_by_id(int ID) {

            return std::find_if(objLecturers.begin(), objLecturers.end(), (&)(Lecturer lecturer) {return lecturer.ID() == ID; });
        }
        auto find_subject_by_id(int ID) {

            return std::find_if(objSubjects.begin(), objSubjects.end(), (&)(Subject subject) {return subject.ID() == ID; });
        }
        auto find_department_by_id(int ID) {
            return std::find_if(objDepartments.begin(), objDepartments.end(), (&)(Department department) {return department.ID() == ID; });
        }
        auto find_class_by_id(int ID) {
            return std::find_if(objClasses.begin(), objClasses.end(), (&)(Class objClass) {return objClass.ID() == ID; });
        }
    public:
        University() {
            objSubjects.emplace_back(Subject{"Programming" });
            objSubjects.emplace_back(Subject{"Web App Development" });
            objSubjects.emplace_back(Subject{"Networking" });
            objDepartments.emplace_back(Department{"Winter Gardens" });
            objDepartments.emplace_back(Department{"South West Skills Campus" });
            objLecturers.emplace_back(Lecturer{"Sean Shearing", 1, 1 });
            objClasses.emplace_back(Class{ 1 });
            objClasses.emplace_back(Class{ 1 });

            //Test data
            add_student("Jack Cole", 1, 56);
            add_student("Sam Cole", 1, 78);
            add_student("Devon Phillips", 1, 91);
            add_student("Thomas Saunders", 1, 54);
            add_student("Jack Kimmins", 2, 58);
            add_student("Oli Stahmer", 2, 91);
            add_student("George Bradley", 2, 78);   
            add_student("Joshua Price", 2, 54);                
        }

        auto best_performing_class() {
            return std::max_element(objClasses.begin(), objClasses.end(), (&)(const Class& lhs, const Class& rhs)
                {
                    return lhs.total_score() < rhs.total_score();
                });
        }
        auto worst_performing_class() {
            return std::min_element(objClasses.begin(), objClasses.end(), (&)(const Class& lhs, const Class& rhs)
                {
                    return lhs.total_score() < rhs.total_score();
                });
        }
        double average(std::vector<Student>& students) {
            
            auto sum = std::transform_reduce(students.begin(), students.end(), 0.0, std::plus<>(),
                ()(auto& student) { return student.result(); });
            return sum / objStudents.size();
        }
        auto find_students_in_class(int classID) {
            std::vector<Student>students;
            for (const Student& objStudent : objStudents) {

                if (objStudent.classID() == classID) {
                    students.push_back({ objStudent });
                }
            }
            return students;
        }

        int class_median_score(int class_id) {
            std::vector<int>scores;
            auto students = find_students_in_class(class_id);
            for (Student& student : students) {
                scores.push_back(student.result());
            }
            std::sort(scores.begin(), scores.end()); 
            if (scores.size() % 2 == 0) 
            {
                return (scores.at(scores.size() / 2  - 1) + scores.at(scores.size()/2)) / 2;                        
            }
            return scores.at(scores.size()/2);
        }
        void view_class_students(int classID) {
            auto students = find_students_in_class(classID);
            display_students(students);
            
        }
        void display_students(std::vector<Student>& objStudents) {
            for (const Student& objStudent : objStudents) {
                std::cout << "ID: " << objStudent.ID() << " | ";
                std::cout << "Name: " << objStudent.name() << " | ";
                std::cout << "Score: " << objStudent.result() << "n";
            }
        }
        auto highest_mark_by_student(std::vector<Student>& objStudents) {
            return std::max_element(objStudents.begin(), objStudents.end(), (&)(const Student& lhs, const Student& rhs)
                {
                    return lhs.result() < rhs.result();
                });
        }
        auto lowest_mark_by_student(std::vector<Student>& objStudents) {
            return std::min_element(objStudents.begin(), objStudents.end(), (&)(const Student& lhs, const Student& rhs)
                {
                    return lhs.result() < rhs.result();
                });
        }
       
        void class_report(int class_id) 
        {
            auto students = find_students_in_class(class_id);
            auto highestScorer = highest_mark_by_student(students);
            auto lowestScorer = lowest_mark_by_student(students);
            std::cout << "Highest scorer: Student ID: " << highestScorer->ID() << " | Name: " << highestScorer->name() << " | score: " << highestScorer->result() <<"n";         
            std::cout << "Lowest scorer: Student ID: " << lowestScorer->ID() << " | Name: " << lowestScorer->name() << " | score: " << lowestScorer->result() << "n";
            std::cout << "Median score: " << class_median_score(class_id) << "n";
            std::cout << "Average score: " << average(students) << "n";
        }

        void overall_classes_report() {
            auto bestClass = best_performing_class();
            auto worstClass = worst_performing_class();
            std::cout << "nBest Performing Class(s)...n";
            std::cout << "ID: " << bestClass->ID() << " | Lecturer: " << find_lecturer_by_id(bestClass->lecturer_ID())->name() << " | score: " << bestClass->total_score() << "n";
            std::cout << "n";
            std::cout << "Worst Performing Class(s)...n";
            std::cout << "ID: " << worstClass->ID() << " | Lecturer: " << find_lecturer_by_id(worstClass->lecturer_ID())->name() << " | score: "<< worstClass->total_score() << "n";
            std::cout << "n";
        }
       
        void display_classes() {
            for (const Class& objClass : objClasses)
            {
                auto lecturer = find_lecturer_by_id(objClass.lecturer_ID());
                auto subject = find_subject_by_id(objClass.lecturer_ID());
                auto department = find_department_by_id(lecturer->departmentID());
                auto students = find_students_in_class(objClass.ID());
               
                std::cout << "Class ID: " << objClass.ID() << "n";
                std::cout << "Class Size: " << objClass.size() << "n";
                std::cout << "Lecturer: " << lecturer->name() << "n";
                std::cout << "Subject: " << subject->name() << "n";
                std::cout << "Department: " << department->name() << "nn";     
            }
        }
  
        bool is_class_id_valid(int class_id) {
            if (auto it = std::find_if(objClasses.begin(), objClasses.end(), (&)(auto &objClass) {
                return class_id == objClass.ID();
                }) == objClasses.end()) {
                return false;
            }
            return true;
        }

        bool add_student(std::string name, int classID, int result) {

            if (is_class_id_valid(classID)) {
                objStudents.emplace_back(Student{ name, classID, result });
                find_class_by_id(classID)->total_score(result);
                find_class_by_id(classID)->increment_size();
                return true;
            }
            return false;
        }
    };

int main()
{
    University objUniversity;

    //viewing class 1
    std::cout << "View class students...n";
    objUniversity.view_class_students(1);

    std::cout << "Class report...n";
    objUniversity.class_report(1);

    //viewing class 2
    std::cout << "View class students...n";
    objUniversity.view_class_students(2);

    std::cout << "Class report...n";
    objUniversity.class_report(2);

    std::cout << "Overall classes report...n";
    objUniversity.overall_classes_report();
    
    //Run via menu system
    //menu_options(objUniversity);
}