C++: 練習:OOP総合

最終更新:2026-08-31

35レッスンで、C++の核心を学びました。

今こそ、総合的な応用です。

このレッスンでは、学生管理システムをゼロから実装 —— コードで理解を深める。


1. プロジェクト概要

(1) 1.1 機能一覧

学生管理システムを実装、以下の機能を持つ:

機能モジュール 機能
学生管理 追加、削除、修正、検索
成績管理 追加、削除、計算
ファイル入出力 ファイルに保存、ファイルから読込
統計機能 学生の成績統計

(2) 1.2 クラス設計

学生のクラス階層:

TEXT 📖 参照専用
Student(基底クラス)
   ↑ 
UndergraduateStudent(学部生)
GraduateStudent(大学院生)

Course(コース)
Grade(成績)

ManagementSystem(管理システム)
   - vector<Student*> students
   - vector<Course> courses


2. 基本実装

(1) 2.1 学生クラス

例:学生クラス(難易度 ⭐⭐)

TEXT 📖 参照専用
#include <iostream>
#include <string>
#include <vector>

// 基底クラス
class Student {
protected:
    std::string id; // 学籍番号
    std::string name; // 名前
    int age; // 年齢
    std::string major; // 専攻

public:
    // コンストラクタ
    Student(const std::string& id, const std::string& name, int age, const std::string& major)
        : id(id), name(name), age(age), major(major) {}
    
    // 情報を表示(仮想関数)
    virtual void display() const {
        std::cout << "学籍番号:" << id << std::endl;
        std::cout << "名前:" << name << std::endl;
        std::cout << "年齢:" << age << std::endl;
        std::cout << "専攻:" << major << std::endl;
    }
    
    // デストラクタ(仮想)
    virtual ~Student() {}
    
    // Getter
    std::string getId() const { return id; }
    std::string getName() const { return name; }
};

// 学部生
class UndergraduateStudent : public Student {
private:
    int year; // 学年(1-4)

public:
    UndergraduateStudent(const std::string& id, const std::string& name, 
        int age, const std::string& major, int year)
        : Student(id, name, age, major), year(year) {}
    
    void display() const override {
        Student::display();
        std::cout << "学年:" << year << "年生" << std::endl;
        std::cout << "タイプ:学部生" << std::endl;
    }
};

// 大学院生
class GraduateStudent : public Student {
private:
    std::string advisor; // 指導教員

public:
    GraduateStudent(const std::string& id, const std::string& name,
        int age, const std::string& major, const std::string& advisor)
        : Student(id, name, age, major), advisor(advisor) {}
    
    void display() const override {
        Student::display();
        std::cout << "指導教員:" << advisor << std::endl;
        std::cout << "タイプ:大学院生" << std::endl;
    }
};

(2) 2.2 管理システムクラス

例:管理システム(難易度 ⭐⭐⭐)

▶ サンプル 1:STL コンテナの活用(難易度 ⭐)

CPP
class ManagementSystem {
private:
    std::vector<Student*> students; // ポインタの配列(ポリモーフィズム)

public:
    // 学生を追加
    void addStudent(Student* student) {
        students.push_back(student);
        std::cout << "学生を追加しました!" << std::endl;
    }
    
    // 学生を検索
    Student* findStudent(const std::string& id) {
        for (auto s : students) {
            if (s->getId() == id) {
                return s;
            }
        }
        return nullptr;
    }
    
    // 全学生を表示
    void displayAll() const {
        if (students.empty()) {
            std::cout << "学生がいません..." << std::endl;
            return;
        }
        
        for (auto s : students) {
            s->display();
            std::cout << "-------------------" << std::endl;
        }
    }
    
    // デストラクタ(メモリ解放)
    ~ManagementSystem() {
        for (auto s : students) {
            delete s;
        }
    }
};

// メイン関数
int main() {
    ManagementSystem sys;
    
    // 学生を追加
    sys.addStudent(new UndergraduateStudent("001", "Alice", 20, "情報工学", 3));
    sys.addStudent(new GraduateStudent("002", "Bob", 24, "情報工学", "Dr. Smith"));
    
    // 全学生を表示
    sys.displayAll();
    
    return 0;
}
▶ 試してみよう

出力:

TEXT 📖 参照専用
学生を追加しました!
学生を追加しました!
学籍番号:001
名前:Alice
年齢:20
専攻:情報工学
学年:3年生
タイプ:学部生
-------------------
学籍番号:002
名前:Bob
年齢:24
専攻:情報工学
指導教員:Dr. Smith
タイプ:大学院生
-------------------


3. 機能拡張

(1) 3.1 機能一覧

システムに以下の機能を追加:

機能 実装内容
検索 学籍番号で検索
修正 学籍番号で検索し、情報を修正
成績管理 Grade クラスを追加
ファイル保存 ofstream でファイルに書き出し
ファイル読込 ifstream でファイルから読込

(2) 3.2 成績クラス

TEXT 📖 参照専用
// 成績クラス
class Grade {
private:
    std::string courseId; // コースID
    std::string courseName; // コース名
    double score; // 点数

public:
    Grade(const std::string& courseId, const std::string& courseName, double score)
        : courseId(courseId), courseName(courseName), score(score) {}
    
    void display() const {
        std::cout << courseName << ":" << score << "点" << std::endl;
    }
    
    double getScore() const { return score; }
};

// 学生クラスに成績を追加
class Student {
    // ... 既存メンバー ...
    std::vector<Grade> grades; // 成績リスト

public:
    void addGrade(const Grade& grade) {
        grades.push_back(grade);
    }
    
    void displayGrades() const {
        if (grades.empty()) {
            std::cout << "成績がありません..." << std::endl;
            return;
        }
        
        std::cout << name << "の成績:" << std::endl;
        for (const auto& g : grades) {
            g.display();
        }
    }
    
    double getAverage() const {
        if (grades.empty()) return 0.0;
        
        double sum = 0;
        for (const auto& g : grades) {
            sum += g.getScore();
        }
        return sum / grades.size();
    }
};

(3) 3.3 ファイル入出力

ファイル保存:

CPP
void saveToFile(const std::string& filename) {
    std::ofstream file(filename);
    if (!file) {
        std::cout << "ファイルを開けません..." << std::endl;
        return;
    }
    
    for (auto s : students) {
        file << s->getId() << ","
             << s->getName() << ","
             << s->getAge() << std::endl;
    }
    
    file.close();
    std::cout << "ファイルに保存しました!" << std::endl;
}

ファイル読込:

TEXT 📖 参照専用
void loadFromFile(const std::string& filename) {
    std::ifstream file(filename);
    if (!file) {
        std::cout << "ファイルがありません..." << std::endl;
        return;
    }
    
    std::string id, name, ageStr;
    while (std::getline(file, id, ',')) {
        std::getline(file, name, ',');
        std::getline(file, ageStr);
        int age = std::stoi(ageStr);
        
        // 簡易版(実際は学部生/大学院生の判定が必要)
        students.push_back(new UndergraduateStudent(id, name, age, "未定", 1));
    }
    
    file.close();
    std::cout << "ファイルから読み込みました!" << std::endl;
}


4. ユーザーインターフェース実装

(1) 4.1 メニュー

CPP
void showMenu() {
    std::cout << "\n===== 学生管理システム =====" << std::endl;
    std::cout << "1. 学生追加" << std::endl;
    std::cout << "2. 学生検索" << std::endl;
    std::cout << "3. 全学生表示" << std::endl;
    std::cout << "4. 成績追加" << std::endl;
    std::cout << "5. 成績表示" << std::endl;
    std::cout << "6. ファイル保存" << std::endl;
    std::cout << "0. 終了" << std::endl;
    std::cout << "========================" << std::endl;
    std::cout << "選択してください:";
}

int main() {
    ManagementSystem sys;
    int choice;
    
    do {
        showMenu();
        std::cin >> choice;
        
        switch (choice) {
            case 1:
                // 学生追加処理
                break;
            case 2:
                // 学生検索処理
                break;
            case 3:
                sys.displayAll();
                break;
            case 0:
                std::cout << "終了します!" << std::endl;
                break;
            default:
                std::cout << "無効な選択です..." << std::endl;
        }
    } while (choice != 0);
    
    return 0;
}


5. プロジェクト拡張

(1) 5.1 機能一覧

機能 難易度 説明
入力検証 ⭐⭐ 無効な入力をチェック、再入力
統計機能 ⭐⭐ 平均点、最高点、最低点
例外処理 ⭐⭐⭐ try-catch でエラー処理
GUI ⭐⭐⭐⭐ Qt や wxWidgets を使用
データベース ⭐⭐⭐⭐ SQLite や MySQL を使用

(2) 5.2 コード最適化

最適化 説明
スマートポインタ unique_ptr でメモリ管理
const 参照 const で読み取り専用
例外処理 try-catch でファイルエラー処理
演算子オーバーロード << をオーバーロードして出力を簡潔に

▶ サンプル 2:図形階層と仮想関数(難易度 ⭐⭐)

CPP
#include <iostream>
#include <string>

class Shape {
public:
    virtual double area() const = 0;
    virtual std::string name() const = 0;
    virtual ~Shape() = default;
};

class Rectangle : public Shape {
private:
    double width, height;
public:
    Rectangle(double w, double h) : width(w), height(h) {}
    double area() const override { return width * height; }
    std::string name() const override { return "長方形"; }
};

class Circle : public Shape {
private:
    double radius;
public:
    Circle(double r) : radius(r) {}
    double area() const override { return 3.14159 * radius * radius; }
    std::string name() const override { return "円"; }
};

int main() {
    Shape* shapes[] = {
        new Rectangle(4.0, 5.0),
        new Circle(3.0),
        new Rectangle(2.0, 3.0)
    };

    for (Shape* s : shapes) {
        std::cout << s->name() << "の面積: " << s->area() << std::endl;
        delete s;
    }

    return 0;
}
▶ 試してみよう

出力:

TEXT 📖 参照専用
長方形の面積: 20
円の面積: 28.2743
長方形の面積: 6

▶ サンプル 3:ポリモーフィックコンテナ(難易度 ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <string>

class Animal {
public:
    virtual void speak() const = 0;
    virtual ~Animal() = default;
};

class Dog : public Animal {
public:
    void speak() const override {
        std::cout << "ワン!" << std::endl;
    }
};

class Cat : public Animal {
public:
    void speak() const override {
        std::cout << "ニャー!" << std::endl;
    }
};

int main() {
    std::vector<Animal*> animals;
    animals.push_back(new Dog());
    animals.push_back(new Cat());
    animals.push_back(new Dog());

    for (const Animal* a : animals) {
        a->speak();
    }

    for (Animal* a : animals) {
        delete a;
    }

    return 0;
}
▶ 試してみよう

出力:

TEXT 📖 参照専用
ワン!
ニャー!
ワン!

❓ よくある質問

Q: ポインタを使う理由は? A: ポリモーフィズムを実現するため。vector<Student> だとオブジェクトがスライス、基底クラスの関数しか呼ばれる。


Q: メモリリークを防ぐには? A: スマートポインタ(unique_ptr)を使ってメモリ管理、手動 delete を避ける。


Q: どのファイル形式で保存する? A: 選択肢:

  • CSV:簡単、でも機能に制限がある
  • JSON:機能豊富、別途ライブラリが必要
  • バイナリ:高速、でも読めない

Q: 日本語を扱うには? A: C++ で日本語を扱う方法:

  • wstring + wcout を使う
  • UTF-8 エンコーディングを使う
  • 外部ライブラリ(iconv など)を使う

📖 まとめ

項目 内容
クラス設計 基底クラス + 派生クラス、継承でコード再利用
ポリモーフィズム ポインタ・参照で仮想関数を呼び出す
ファイル入出力 ofstream で書き出し、ifstream で読込
メモリ管理 デストラクタでメモリ解放(スマートポインタ推奨)
メニュー do-while + switch で実装

推奨:


📝 練習問題

  1. 初級(難易度 ⭐): Book クラスを定義し(タイトル、著者、価格)、3つのオブジェクトを作成して情報を出力。

  2. 中級(難易度 ⭐⭐): 継承階層を実装:Shape(基底クラス、純粋仮想関数 area())→ Circle と Rectangle を派生クラス。ポリモーフィズムで area() を呼び出し。

  3. 上級(難易度 ⭐⭐⭐): 「オブザーバーパターン」を実装:Subject クラスが Observer リストを持ち、Observer は基底クラス。Subject が全 Observer に通知。



6. 🚀 次のステップ

おめでとう!フェーズ5(OOP)完了! 次は フェーズ6(STL応用) へ —— より深い C++ の世界へ!

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%