Degree and Non-degree seeking Students
Problem
Write a program for BYYou University Registrar to keep student profiles
BYYou University offers courses, but not degrees
A BYYou student has
a name
a unique student ID
a list of courses
Courses are worth 3 credits each and can only be taken once
Part I
Create a class that contains data members for
a name
a unique student ID
a list of courses
Member functions should include functionality to
check if a course has been taken
get total credits taken
add a course to the course list
get a string representation as seen here: Jane Doe; ID# 159817; 35 credits
Note that the ID is assigned by the program
Part II
Part III
Great news! BYYou now offers a number of Bachelor degrees!
Create a Degree-Seeking Student class which includes
This derived class should include functionality to test if a student is eligible to graduate
The student summary should also include the program name, credit requirement, and eligibility for graduation
Part IV
Allow the Registrar to input a new student’s program name and credit requirement (or “NONE” if student is non-degree seeking)
Make an object of the base or derived class appropriate to whether the student is degree-seeking
Solution
Source.cpp
/*
Examples of test case classes include (you should have specific test cases):
Test cases should create both a degree-seeking and a non-degree seeking student
They should try to add classes that have already been added
They should create degree-seeking students that are both eligible and ineligible to graduate (check border cases)
They should test when no students are added
*/
#include <iostream>
#include <string>
#include "DegreeStudent.h"
#include "Student.h"
using namespace std;
Student * create_student(string name);
int main()
{
vector<Student *> students;
/* BASE CLASS TEST CODE
Student * student_A = new Student("Harry Potter");
Student * student_B = new Student("Hermione Granger");
student_A->add_course("Potions");
cout << student_A->to_string() << endl;
cout << student_B->to_string() << endl;
*/
/* DERIVED CLASS TEST CODE
DegreeStudent * ds_student_C = new DegreeStudent("Neville Longbottom", "Herbology", 2);
ds_student_C->add_course("Herbology 101");
cout << ds_student_C->to_string() << endl;
// A DegreeStudent IS-A Student, thus by the substitution principle we can put a
// DegreeStudent * where a Student * is expected
Student * student_ptr = ds_student_C;
// Without "virtual", this would call the to_string() function of the base class
// With "virtual", the to_string() function of the more specific class is called
cout << student_ptr->to_string() << endl;
// We can call is_eligible_to_graduate() because ds_student_C is of type DegreeStudent *
// If ds_student_C were of type Student *, we could not because a Student object
// (or even a DegreeStudent object being pointed to by a Student *) doesn't have that function
if (ds_student_C->is_eligible_to_graduate())
{
cout << "Mmmm, graduate you will" << endl;
}
// The substitution principle allows us to put a DegreeStudent * any place that a Student * is expected
students.push_back(ds_student_C);
system("pause");
*/
// 1. INPUT STUDENTS AND THEIR COURSES
string input;
cout << "Student name: ";
getline(cin, input);
while (input != "END")
{
//remember, create_student is a static function, not a class member function
Student * new_student = create_student(input);
students.push_back(new_student);
cout << endl;
cout << "Student name: ";
getline(cin, input);
}
cout << "***********************" << endl;
// 2. OUTPUT STUDENT SUMMARIES
for (int i = 0; i < students.size(); i++)
{
// With "virtual" the more specific to_string() function will be called,
// depending on whether students[i] points to a Student object or a DegreeStudent
cout << students[i]->to_string() << endl;
}
system("pause");
return 0;
}
Student * create_student(string name)
{
//This pointer will be what we return from the function and needs to
//initialized appropriately depending on the user's input to a Student * or a DegreeStudent *
Student * new_student = NULL;
string input;
cout << "Program: ";
getline(cin, input);
if (input == "NONE")
{
// No program means the student is not degree-seeking
new_student = new Student(name);
}
else
{
// Otherwise student is degree seeking and we need more info
int credit_requirement;
cout << "Credits required: ";
cin >> credit_requirement;
cin.sync(); // We used cin >>, so we need to read the '\n' character
// By the substitution principle, we can put a DegreeStudent * in for a Student *
// Remember, a DegreeStudent IS-A Student
new_student = new DegreeStudent(name, input, credit_requirement);
}
cout << "Course taken: ";
getline(cin, input);
while (input != "END")
{
// Whether a DegreeStudent or just a Student, both have access to the base class function add_course()
new_student->add_course(input);
cout << "Course taken: ";
getline(cin, input);
}
return new_student;
}
Student.h
#include <string>
#include <vector>
using namespace std;
// #pragma once is the same as #ifndef ..., #define ..., and #endif
#pragma once
class Student
{
public:
// CONSTRUCTOR
/*
Constructor that creates a new Student object given a name
@param name to assign to the new Student object
*/
Student(string _name);
// OTHER MEMBER FUNCTIONS
/*
Test if *this* student has taken a course
@param course name of course to test for
@return true if course is in list of taken courses; false otherwise
*/
bool has_taken_course(string course) const;
/*
@return total credits, which is 3*number of courses taken
*/
int get_total_credits() const;
/*
Adds a course to list of courses taken IF and only if course has not already been taken
@param course course name to add
@return 0 if added; -1 if course already present
*/
int add_course(string course);
/*
@return a string representation of this String object
*/
//virtual means "check any derived class first to see if this is overwritten"
virtual string to_string() const;
private:
// DATA MEMBERS
string name;
int student_id;
vector<string> course_list;
// static variables exist once for all objects of the class
static int next_id;
};
Student.cpp
#include <sstream>
#include "Student.h"
// Initialize the static variable
int Student::next_id = 0;
Student::Student(string _name)
{
name = _name;
student_id = next_id; // here is our static variable at work
next_id++;
}
bool Student::has_taken_course(string course) const
{
for (int i = 0; i < course_list.size(); i++)
{
if (course_list[i] == course)
{
return true;
}
}
// Never found it
return false;
}
int Student::get_total_credits() const
{
// We assume all courses are the same number of credits
return course_list.size() * 3;
}
int Student::add_course(string course)
{
// Don't add course if it's already in the list
if (has_taken_course(course))
{
return -1;
}
else
{
course_list.push_back(course);
return 0;
}
}
string Student::to_string() const
{
// use ostringstream to build a string that is a composite of strings and other variable types
ostringstream strm;
strm << name << "; ID# " << student_id << "; " << get_total_credits() << " credits";
return strm.str();
}
DegreeStudent.h
// To inherit from Student, we need to know what a Student is
#include "Student.h"
#pragma once
// this is where we specify that a DegreeStudent IS-A Student and inherits everything a Student has
class DegreeStudent : public Student
{
public:
// CONSTRUCTOR
/*
Constructor to make a DegreeStudent from a name, program, and a credit requirement
@param _name name to give to *this* student
@param _program_name name to assign to *this* student's program
@param _credit_requirement value to set as *this* student's credit requirement
*/
DegreeStudent(string _name, string _program_name, int _credit_requirement);
// OTHER MEMBER FUNCTIONS
/*
@return true if *this* student has taken greater than or equal to the credit requirement
*/
bool is_eligible_to_graduate() const;
/*
@return a string representation of *this* DegreeStudent objeect
*/
//virtual means "check any derived class first to see if this is overwritten"
virtual string to_string() const;
private:
// DATA MEMBERS
string program_name;
int credit_requirement;
// Note that all other data members (e.g., name, id, course_list) are inherited
};
DegreeStudent.cpp
#include <sstream>
#include "DegreeStudent.h"
DegreeStudent::DegreeStudent(string _name, string _program_name, int _credit_requirement)
:Student(_name), program_name(_program_name), credit_requirement(_credit_requirement)
// Everything after the ':' is the initializer list and includes a call to the base class constructor
// Otherwise we couldn't access the base class data members to initialize them
{
}
// OTHER MEMBER FUNCTIONS
bool DegreeStudent::is_eligible_to_graduate() const
{
// This is a call to the base class member function
// We don't need to specify Student:: because it's implied when there is no overwriting
// function in the derived DegreeStudent class
int total_credits = get_total_credits();
return (total_credits >= credit_requirement);
}
string DegreeStudent::to_string() const
{
// use ostringstream to build a string that is a composite of strings and other variable types
ostringstream strm;
// first we want what's already put together by the base class to_string() function
strm << Student::to_string();
// then we add stuff specific to the derived class
strm << "; " << program_name << "; Credits required: " << credit_requirement;
if (is_eligible_to_graduate())
{
strm << " - ELIGIBLE TO GRADUATE!";
}
return strm.str();
}
Back to top