成績管理システムの実装と改善

この記事では、C++を使用して成績管理システムを設計・実装する方法について説明します。以下のセクションで、クラス設計、コード例、およびいくつかの問題に対する回答を提供します。

1. システム概要

成績管理システムは、学生の成績を入力、出力、並べ替え、統計情報を計算する機能を備えています。

1.1 主なクラス設計

主なクラスである GradeManager には、次のメソッドが含まれます:

  • addGrades(): 成績を追加します。
  • displayGrades(): 現在の成績を表示します。
  • sortGrades(bool ascending): 成績を昇順または降順に並べ替えます。
  • getMinGrade(): 最低点を返します。
  • getMaxGrade(): 最高点を返します。
  • getAverageGrade(): 平均点を計算します。
  • printStats(): 各スコア範囲の人数と比率を表示します。

2. コード例

2.1 ヘッダーファイル: GradeManager.hpp


#pragma once

#include <string>
#include <vector>
#include <array>

class GradeManager {
public:
    GradeManager(const std::string& subjectName);
    void addGrades(int count);
    void displayGrades() const;
    void sortGrades(bool ascending = false);
    int getMinGrade() const;
    int getMaxGrade() const;
    double getAverageGrade() const;
    void printStats();

private:
    void calculateStats();
    
    std::string subject;
    std::vector<int> scores;
    std::array<int, 5> scoreCounts;
    std::array<double, 5> scoreRates;
    bool isDataUpdated;
};

2.2 実装ファイル: GradeManager.cpp


#include "GradeManager.hpp"
#include <iostream>
#include <algorithm>
#include <numeric>

GradeManager::GradeManager(const std::string& subjectName)
    : subject{subjectName}, isDataUpdated{true} {
    scoreCounts.fill(0);
    scoreRates.fill(0.0);
}

void GradeManager::addGrades(int count) {
    if (count <= 0) return;

    int tempScore;
    for (int i = 0; i < count; ++i) {
        std::cin >> tempScore;
        if (tempScore < 0 || tempScore > 100) continue;
        scores.push_back(tempScore);
    }
    isDataUpdated = true;
}

void GradeManager::displayGrades() const {
    for (auto s : scores) std::cout << s << ' ';
    std::cout << '\n';
}

void GradeManager::sortGrades(bool ascending) {
    if (ascending)
        std::sort(scores.begin(), scores.end());
    else
        std::sort(scores.begin(), scores.end(), std::greater<int>());
}

int GradeManager::getMinGrade() const {
    if (scores.empty()) return -1;
    return *std::min_element(scores.begin(), scores.end());
}

int GradeManager::getMaxGrade() const {
    if (scores.empty()) return -1;
    return *std::max_element(scores.begin(), scores.end());
}

double GradeManager::getAverageGrade() const {
    if (scores.empty()) return 0.0;
    return std::accumulate(scores.begin(), scores.end(), 0.0) / scores.size();
}

void GradeManager::printStats() {
    if (isDataUpdated) calculateStats();
    std::cout << "教科名: " << subject << "\n";
    std::cout << "平均点: " << getAverageGrade() << "\n";
    std::cout << "最高点: " << getMaxGrade() << "\n";
    std::cout << "最低点: " << getMinGrade() << "\n";

    const std::array<std::string, 5> ranges{
        "[0,60)", "[60,70)", "[70,80)", "[80,90)", "[90,100]"};

    for (size_t i = 0; i < ranges.size(); ++i)
        std::cout << ranges[i] << ": " << scoreCounts[i]
                  << "人 (" << scoreRates[i] * 100 << "%)\n";
}

void GradeManager::calculateStats() {
    if (scores.empty()) return;
    scoreCounts.fill(0);
    scoreRates.fill(0.0);

    for (auto s : scores) {
        if (s < 60) ++scoreCounts[0];
        else if (s < 70) ++scoreCounts[1];
        else if (s < 80) ++scoreCounts[2];
        else if (s < 90) ++scoreCounts[3];
        else ++scoreCounts[4];
    }

    for (size_t i = 0; i < scoreRates.size(); ++i)
        scoreRates[i] = static_cast<double>(scoreCounts[i]) / scores.size();

    isDataUpdated = false;
}

2.3 テストコード: main.cpp


#include "GradeManager.hpp"
#include <iostream>

int main() {
    GradeManager manager("数学");
    std::cout << "成績を入力してください:\n";
    manager.addGrades(5);

    std::cout << "成績一覧:\n";
    manager.displayGrades();

    std::cout << "並べ替え後:\n";
    manager.sortGrades();
    manager.displayGrades();

    std::cout << "統計情報:\n";
    manager.printStats();
}

3. 問題とその解決策

以下は、実装中に考えられるいくつかの問題とその回答です。

3.1 問題: スコア範囲の設定

質問: スコア範囲をどのように設定すればよいですか?
回答: スコア範囲は、std::array<int, 5> を使用して定義できます。それぞれの範囲に対応するインデックスを持つことで、効率的にデータを処理できます。

3.2 問題: データ更新時の通知

質問: データが更新されたときに、統計情報を再計算させる必要がありますか?
回答: はい。フラグ (isDataUpdated) を使用して、データが変更された場合にのみ再計算を行います。

3.3 問題: 中央値の計算

質問: 中央値を計算する方法はありますか?
回答: 次のように、中央値を計算する関数を追加できます:


double GradeManager::getMedian() const {
    if (scores.empty()) return -1.0;
    std::vector<int> sortedScores = scores;
    std::sort(sortedScores.begin(), sortedScores.end());
    size_t mid = sortedScores.size() / 2;
    if (sortedScores.size() % 2 == 0)
        return (sortedScores[mid - 1] + sortedScores[mid]) / 2.0;
    return sortedScores[mid];
}

タグ: C++ OOP DesignPatterns

5月29日 07:49 投稿