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 で実装 |
推奨:
- プロジェクトで総合的に応用、理解を深める
- 機能を実装しながら、継承やポリモーフィズムを実践
- 問題に遭遇したら、解決策を調べる
📝 練習問題
-
初級(難易度 ⭐): Book クラスを定義し(タイトル、著者、価格)、3つのオブジェクトを作成して情報を出力。
-
中級(難易度 ⭐⭐): 継承階層を実装:Shape(基底クラス、純粋仮想関数 area())→ Circle と Rectangle を派生クラス。ポリモーフィズムで area() を呼び出し。
-
上級(難易度 ⭐⭐⭐): 「オブザーバーパターン」を実装:Subject クラスが Observer リストを持ち、Observer は基底クラス。Subject が全 Observer に通知。
6. 🚀 次のステップ
おめでとう!フェーズ5(OOP)完了! 次は フェーズ6(STL応用) へ —— より深い C++ の世界へ!