Программа "журнал", который отслеживает работу учащихся в классе, выводит учащихся с высоким и самым низким уровнем


Эта программа содержит два класса, Student и Course, что позволит пользователю анализировать работу студентов конкретного курса.

  • Student класс будет содержать личные данные членов, состоящих из студентов информацию (наименование, сорта, класса и т. д.)

  • Course класс будет действовать в рамках программы, которая будет управлять всей класса соответственно.

#include <cstdlib>
#include <cmath>
#include <cctype>
#include <iostream>
#include <iomanip>
#include <limits>
#include <fstream>
#include <string>
using namespace std;
ofstream toFile;
class Student
{
     private:
        string name;
        int project[6];
        int exam[2];
        int quiz[5];
        double project_avg;
        double exam_avg;
        double quiz_avg;
        int finalExam;
        double average;
        string grade;
     public: 
        Student();
        void get_student_scores();
        void compute_student_stats();
        void determine_student_grade();
        void display_student_name_gpa_grade();
        double student_average();
};

class Course
{
    private:
        Student student[7];
        int index_of_highest;
        int index_of_lowest;
    public:
        Course();
        ~Course();
        void get_grades();
        void evaluate_class();
        void determine_index_of_highest_lowest();
        void display_highest();
        void display_lowest();
        void display_stats();
};

Student::Student()
{
    name = " ";

    for(int i = 0; i<6; i++)
        project[i] = 0;

    for(int i = 0; i<2; i++)
        exam[i] = 0;

    for(int i = 0; i<5; i++)
        quiz[i] = 0;

    average, project_avg, exam_avg, quiz_avg = 0.0;
    finalExam = 0;
    grade = " ";
}

void Student::get_student_scores()
{
    try
    {
        cout << "\n Enter Student Name: \n";
        cin>>name;

        for(int i = 0; i<6; i++)
        {
            cout<<"Enter Student project grade #"<<i+1<<":\n";
            cin>>project[i];
        }

        for(int i = 0; i<2; i++)
        {
            cout<<"Enter Student exam grade #"<<i+1<<":\n";
            cin>>exam[i];
        }

        for(int i = 0; i<5; i++)
        {
            cout<<"Enter Student quiz grade #"<<i+1<<":\n";
            cin>>quiz[i];
        }

        cout << "Enter final exam grade: \n";
        cin>>finalExam;    
    }

    catch(...)
    {
        cin.clear(); 
        cin.ignore(numeric_limits<streamsize>::max(), '\n');

        cout<<"Error, invalid input entered. \n";
    }
    return;
}

void Student::compute_student_stats()
{
    cout<<fixed<<showpoint<<setprecision(2);

    for(int i = 0; i<6; i++)
        project_avg += project[i]; 
    project_avg = project_avg/6;
    cout << "Project Average = " << project_avg << "\n";

    for(int i = 0; i<2; i++)
        exam_avg += exam[i]; 
    exam_avg = exam_avg/2;
    cout << "Exam average = " << exam_avg << "\n";

    for(int i = 0; i<5; i++)
        quiz_avg += quiz[i]; 
    quiz_avg = quiz_avg/5;
    cout << "Quiz average = " << quiz_avg << "\n";

    average = (project_avg*0.25) + (exam_avg*0.4) + (quiz_avg*0.1) + (finalExam*0.25);
    cout << "Semester average = " << average << "\n"; 

    return;
}

void Student::determine_student_grade()
{
    grade = "0";
    if(average <= 100 && average >= 93)
        grade = "A";
    //range from 92 - 89
    else if(average >= 89)
        grade = "A-";
    //range from 88 - 87
    else if(average >= 87)
        grade = "B+";
    //range from 89 - 83
    else if(average >= 83)
        grade = "B";
    //range from 82 - 79
    else if(average >= 79)
        grade = "B-";
    //range from 78 - 77
    else if(average >= 77)
        grade = "C+";
    //range from 76 - 73
    else if(average >= 73)
        grade = "C";
    //range from 72 - 69
    else if(average >= 69)
        grade = "C-";
    //range from 68 - 67
    else if(average >= 67)
        grade = "D+";
    //range from 66 - 63
    else if(average >= 63)
        grade = "D";
    //range from 62 - 57   
    else if(average >= 57)
        grade = "D-";
    //range below 57    
    else if(average <= 57)
        grade = "F";

    cout << "Letter Grade = " << grade << "\n";
    return;
}

void Student::display_student_name_gpa_grade()
{
    const int width1 = 19;
    const int width2 = 17;

    cout<<fixed<<showpoint<<setprecision(2);
    cout << left << setw(width1) << "Student = " << name;
    cout << right << setw(width2) << "Average = " << average;
    cout << right << setw(width2) << "Letter Grade = " << grade << "\n";

    toFile.open("student.txt",ios::app);
    toFile<<fixed<<showpoint<<setprecision(2);
    toFile << left << setw(width1) << "Student = " << name;
    toFile << right << setw(width2) << "Average = " << average;
    toFile << right << setw(width2) << "Letter Grade = " << grade << "\n";
    toFile.close();
    return;
}

double Student::student_average()
{
    return average;
}



Course::Course()
{
    index_of_highest = 0;
    index_of_lowest = 0;
}
Course::~Course()
{
    cout << "Course Object is going out of existence.";
}
void Course::get_grades()
{
    for(int i=0; i<7; i++)
        student[i].get_student_scores();    
    return;
}

void Course::evaluate_class()
{
    for(int i=0; i<7; i++)
    {
        student[i].compute_student_stats();
        student[i].determine_student_grade();
    }
    return;
}

void Course::determine_index_of_highest_lowest()
{
    int tempHighest = 0; 
    int tempLowest = 100;

    for(int i=0; i<7; i++)
    {
        if(student[i].student_average() > tempHighest)
        {
            tempHighest = student[i].student_average();
            index_of_highest = i;
        }
    }

    for(int i=0; i<7; i++)
    {
        if(student[i].student_average() < tempLowest)
        {
            tempLowest = student[i].student_average();
            index_of_lowest = i;
        }
    }
    return;
}


/*==============================================================================
 when displaying the name, grade, and gpa, The very first student, (student[0])
 is not being sent to the txt file "student.txt"
==============================================================================*/
void Course::display_stats()
{    
    for (int i=0; i < 7; i++)
    {
        student[i].display_student_name_gpa_grade();
    }
}

void Course::display_highest()
{
    student[index_of_highest].display_student_name_gpa_grade();
        return;
}

void Course::display_lowest()
{    
    student[index_of_lowest].display_student_name_gpa_grade();
        return;
}

void describeProgram()
{
    cout<<"This program will serve as a grade book for seven students who are"
          " participating in a course, \n and will also analyze the performances"
          "of the students as well. Each student will have six projects (worth 25%"
          " collectively), \n two midterm exams (worth 20% each), and five quizzes (worth 10%"
          " collectively), and a final exam (worth 25%). \n The program will calculate"
          " the class average and will also display the student with the highest"
          " and lowest grade.";  
}

int main(int argc, char** argv) 
{
    Course courseObj;

    describeProgram();
    courseObj.get_grades();
    courseObj.evaluate_class();
    courseObj.determine_index_of_highest_lowest();

    toFile.open("student.txt",ios::out);

    toFile<<fixed<<showpoint<<setprecision(2)<<endl;
    cout<<fixed<<showpoint<<setprecision(2)<<endl;  

    courseObj.display_stats();
    cout << "\n";
    toFile << "\n";

    cout<<"The name, average, and grade of the best student in the class is:\n";
    toFile<<"The name, average, and grade of the best student in the class is:\n";  
    courseObj.display_highest();

    cout<<"The name, average, and grade of the poorest student in the class is:\n";
    toFile<<"The name, average, and grade of the poorest student in the class is:\n";
    courseObj.display_lowest();
    toFile.close();
    system("PAUSE");

    return 0;
}


612
2
задан 7 апреля 2018 в 08:04 Источник Поделиться
Комментарии
3 ответа

Позвольте мне быть первым, чтобы сказать:

Не говори using namespace std;

Вы используете конкретные цифры в размерах массив привлекает мое внимание. Затем,

for(int i = 0; i<6; i++)
project[i] = 0;

Здесь вы предполагаю, что вы знаете, сколько элементов есть, и повторяя "магическое число", а не хотя бы через именованную константу.

Но для обнуления примитивном стиле c такими вы не пишите наследие for петля в любом случае. Использовать

std::fill(std::begin(project),std::end(project), 0);

но это довольно неловко, когда я просто хочу все это, так что я бы на самом деле использовать

boost::range::fill (project, 0);

но, просто не знаю!

Объявить project как тип, который будет автоматически инициализировать его элементы.

int project[6];  // original

становится

std::array<int,6> project;

Аналогично для другой массив выглядит.

У вас также есть name = " "; (один пробел) и я не знаю, если это то, что вы действительно имели в виду. Я предполагаю, что вы имеете в виду имя, чтобы быть пустым (то есть пустой).

Строки инициализировать себя. Если вы хотели инициализировать что-то, кроме пустой, ты бы поставил его в качестве инициализаторов в конструкторе, а не присваивания в теле конструктора. Это правда о все вещи в конструкторе — ничего у вас там должен быть в теле!

Для равнины значений int и Double, вы можете использовать встроенные inititializers элемент данных для хорошего эффекта здесь.

⋮  // inside the class definition
double project_avg = 0.0;
double exam_avg = 0.0;

теперь там ничего не осталось из ваших строителей, чтобы избавиться от всего этого.


Для get_student_scoresэто не то, что мы обычно делаем. Этот вид оперативного и читать не реально для реальных программ. Однако, вы могли бы получать входные данные из файла или что-то подобное.

Дело в том, вы не хотите использовать наследие for петля, вы не хотите использовать индексы массива по одному, но распечатка индекса как часть этой строки означает, что вы должны знать количество. Что учит вас анти-паттерна, который является, почему я заладил в качестве учебного примера.

Позвольте мне изменить его немного, но сохранить ручной ввод с подсказками

for (auto& grade : project) {
cout << "Enter a grade\n";
cin >> grade;
}


для compute_student_statsвы также используете устаревший for петли и инструкция по выполнению работы. Знаете, ваш СТД алгоритмы. Это конкретный случай знаю вашей библиотеки, это очень важная вещь.

Как и прежде, я буду использовать буст версии вместо СТД.

{
using boost::range::accumulate;
project_avg = accumulate(project) / double(project.size());
// ditto for the others

Другая вещь, чтобы отметить: сумма дает целое число, так что придется явно преобразовывать один из аргументов в отдел double или я получу целочисленного деления. Добавление intS будет быстрее, чем преобразование каждого int к double и добавив, таким, каким вы его.


determine_student_grade очень однообразная. Нет встроенного способа, чтобы указать это как case заявление, но я хотел бы использовать структуру данных с отключений и классов, а не каскадом if’ы.

Давайте не будем беспокоиться об этом сейчас; что является более продвинутым.


Student::student_average() должно быть const.

И тривиальной функции, как правило, указаны встроенные в определении класса.


Course конструктор:

Же ноты, как и раньше. Использовать встроенные инициализаторы в определении класса, и даже не писать конструктор. И когда вам нужен конструктор, знаю разницу между инициализации и присваивания.

Для деструктор, я задавался вопросом, почему вы даже нужно. В теле я вижу вы инструментирование это — это действительно повод нужен.


Продолжая, я вижу те же самые вопросы. петли, написание std::max вручную, а не зная, библиотеки и т. д.

Вам не нужно чуть-чуть вернуться в конце функции.

Различные распечатки функции, которые не меняют объект должен быть const.


Я не знаю, что system("PAUSE"); о. Это не портативный. Если он останавливается на экране с прокруткой, зачем вам это нужно, когда вы сделали, и не собирается ничего печатать больше?


toFile не должны быть глобальными. Определить, где он будет открыт, и передавать в функции, которые выводят.

2
ответ дан 8 апреля 2018 в 12:04 Источник Поделиться

Я пропустила determine_student_grade в мой первый ответ. Я вернусь к нему сейчас.

Во-первых, а не зная о класс и членам письмо ученика класса, это должна быть отдельная функция, то учащийся может использовать. Это функция, которая принимает число в качестве входных данных и производит письмо класс как выходной. Он не имеет ничего общего со спецификой этого класса, поэтому он не должен знать ни о чем другом.

Единственное, что он должен знать, передается в качестве входных данных. Результат возвращается.

// not a member of any class
const char* letter_grade (int score) {

Во-вторых, код, который ты написал очень однообразные. Когда у вас есть целая куча повторяющихся блоков, как, что, где только два значения меняются, вытащить эти значения в структуре данных, а затем петлю над ней.

Без суеты, я просто использовал примитивный массив пар.

constexpr std::pair<int, const char*> gradechart[] = {
{ 100, "A+" },
{ 93, "A" },
{ 89, "A-" },
{ 87, "B+" },
{ 83, "B" },
{ 79, "B-" },
{ 77, "C+" },
{ 73, "C"},
{ 69, "C-" },
{ 67, "D+" },
{ 63, "D"},
{ 57, "D-"},
{ -1, "F" }
};

Зная, что я собирался перебрать на этом и остановлюсь, когда у меня матч, я не хочу, чтобы беспокоиться об особых случаях внутри цикла, если он перед первым элементом или еще не нашли на последний элемент. Поэтому я поставил условие проверки явным образом в функцию.

if (score > 9999) throw std::invalid_argument("That's excessive extra credit!");
if (score < 0) throw std::invalid_argument("Can't score worse than nothing!");

Теперь, наконец, займутся реальной работой. Найти значение должно поместиться в отсортированной коллекции является то, что люди нужны, в общем. Можно подумать, что уже в библиотеке, верно?

Найти либо матч или в месте, где элемент должен быть вставлен, если бы не нашли-это примитивная вещь, используемый для поддержания отсортированной коллекции. Это lower_bound, upper_boundи equal_range.

Однако, как обычно, я буду использовать версию наддува вместо std версия, но на этот раз у меня есть еще одна причина для этого. Помимо возможности просто дать ему весь массив без начала и конца вещей, буст ручки ключа другого типа, чем элемент. Это глупо создавать фиктивные записи просто для сравнения ключевого поля! Я хочу найти intНе пара, чье первое значение типа int.

// This is the actual meat of the function
auto lb= boost::range::lower_bound (gradechart, score,
[](const auto& row, int val) { return row.first > val; });

Теперь, так как я сделал предпосылки, я действительно не ожидал lb чтобы быть недействительным. Но я все равно добавил чек:

if (lb == std::end(gradechart)) throw std::logic_error ("should not happen");

И я закончу.

    return lb->second;
}


Сейчас, чтобы проверить и продемонстрировать его, я написал простой main.

using std::cout;

int main(int argc, char* argv[])
{
int count= argc-1;
char** arg = argv+1;
while (count--) {
auto num = atoi (*arg++);
cout << "number: " << num << " letter: " << letter_grade(num) << '\n';
}
}

И я побежал с критических тестов , которые проверяют краям, чтобы убедиться, что диаграммы используется по назначению: 57 это Г - Но 56-это Ф. (обратите внимание, что я добавил В+, в 100 или лучше).

[prompt]graderange.exe 105 100 99 89 57 56 89 1 0
number: 105 letter: A+
number: 100 letter: A+
number: 99 letter: A
number: 89 letter: A-
number: 57 letter: D-
number: 56 letter: F
number: 89 letter: A-
number: 1 letter: F
number: 0 letter: F

1
ответ дан 9 апреля 2018 в 05:04 Источник Поделиться

ОК, я скопировал ваш источник и побежал сам.

Прежде всего, вы заметили, что что-то не так?!


Студент = Средняя Бейкер = -22368577576085144521364296908912192942446188117496865356775424.00 Письмо Класс = Ф

У вас ошибка в Student конструктор:

//  average, project_avg, exam_avg, quiz_avg = 0.0;  // BUG !!
average= project_avg= exam_avg= quiz_avg = 0.0;

Вы задались quiz_avg до 0.0, но другие слова, которые вы использовали, не имеют никакого эффекта! Увидеть запятая оператора за то, что вы на самом деле сделали.


Почему студент не появляется в файл, потому что вы запутались над тем, что вы делали с toFile переменной.

Внутри функции, она выглядит хорошо (хотя странно):

toFile.open("student.txt", ios::app);

toFile.close();

За программу, которая просто печатает результат в файл, открытия и закрытия для каждой части не имеет смысла. Просто откройте его и использовать его. Но это полезно для что-то вроде лог-файла, где у вас есть случайный выход.

Теперь вот проблема: в main у вас есть

toFile.open("student.txt", ios::out);

и никогда не закрыть его там.

Так, в первый раз display_student_name_gpa_grade называется, вы получите сообщение об ошибке от toFile.open (что вы никогда не проверить), потому что она уже открыта! Затем вы закрываете его, который сбрасывает плохом состоянии.


Другая вещь, чтобы узнать о C++ - это РАИИ (также читайте cppreference на эту тему). Это фундаментальная сила C++ и он должен быть использован для хорошего эффекта, где используются ресурсы.

Так что, пока нет глобального объекта файла, который можно открыть и закрыть в различные моменты времени. Объявить переменную, когда вы готовы, чтобы открыть его и используйте конструктор, не .open звоните, чтобы открыть его. Когда он выходит из области видимости, он закрыт, поэтому вы никогда не закрыть его.

В этом случае, вы должны определить его в main когда вы открываете его сейчас, и передавать в функции, которые нужно использовать:

ofstream toFile {"student.txt"};
if (!toFile) throw runtime_error ("Could not open the file");

courseObj.display_stats (toFile);

Я вижу, что в ваш существующий код, toFile будут закрыты, когда он возвращается к main так toFile << "\n"; также никуда не ходить.


незначительные: используйте символ '\n' вместо одного символа строки "\n" при печати строки.

0
ответ дан 10 апреля 2018 в 10:04 Источник Поделиться