C++: Practice: OOP Comprehensive
Last updated: 2026-08-26
In the previous 35 lessons, we've covered many aspects of C++.
Now, it's time to put it all together.
In this lesson, we'll build a complete student management system — from requirements analysis to code implementation, experiencing real software development.
1. Project Requirements Analysis
(1) 1.1 Functional Requirements
The student management system we're building has the following features:
| Feature Module | Specific Functions |
|---|---|
| Student Management | Add, delete, modify, search students |
| Grade Management | Enter grades, calculate averages, ranking |
| Data Persistence | Save to file, load from file |
| User Interface | Menu-driven console interface |
(2) 1.2 Class Design
Based on the requirements, we need to design the following classes:
Student (Base class)
↑ Inherits
UndergraduateStudent (Undergraduate)
GraduateStudent (Graduate student)
Course (Course class)
Grade (Grade class)
ManagementSystem (Management system class)
- vector<Student*> students
- vector<Course> courses
2. Basic Version Implementation
(1) 2.1 Student Class Design
Example: Student Class Basic Version (Difficulty ⭐⭐)
#include <iostream>
#include <string>
#include <vector>
// Student base class
class Student {
protected:
std::string id; // Student ID
std::string name; // Name
int age; // Age
std::string major; // Major
public:
// Constructor
Student(const std::string& id, const std::string& name, int age, const std::string& major)
: id(id), name(name), age(age), major(major) {}
// Virtual function: display info
virtual void display() const {
std::cout << "Student ID: " << id << std::endl;
std::cout << "Name: " << name << std::endl;
std::cout << "Age: " << age << std::endl;
std::cout << "Major: " << major << std::endl;
}
// Virtual destructor
virtual ~Student() {}
// Getter
std::string getId() const { return id; }
std::string getName() const { return name; }
};
// Undergraduate class
class UndergraduateStudent : public Student {
private:
int year; // 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: " << year << std::endl;
std::cout << "Type: Undergraduate" << std::endl;
}
};
// Graduate student class
class GraduateStudent : public Student {
private:
std::string advisor; // 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: " << advisor << std::endl;
std::cout << "Type: Graduate Student" << std::endl;
}
};
(2) 2.2 Management System Class
Example: Management System Basic Version (Difficulty ⭐⭐⭐)
▶ Example 1: Using STL Containers (Difficulty ⭐)
class ManagementSystem {
private:
std::vector<Student*> students; // Student list (polymorphism)
public:
// Add student
void addStudent(Student* student) {
students.push_back(student);
std::cout << "Added successfully!" << std::endl;
}
// Find student
Student* findStudent(const std::string& id) {
for (auto s : students) {
if (s->getId() == id) {
return s;
}
}
return nullptr;
}
// Display all students
void displayAll() const {
if (students.empty()) {
std::cout << "No student information available" << std::endl;
return;
}
for (auto s : students) {
s->display();
std::cout << "-------------------" << std::endl;
}
}
// Destructor: free memory
~ManagementSystem() {
for (auto s : students) {
delete s;
}
}
};
// Test code
int main() {
ManagementSystem sys;
// Add undergraduate
sys.addStudent(new UndergraduateStudent("001", "Zhang San", 20, "Computer Science", 3));
sys.addStudent(new GraduateStudent("002", "Li Si", 24, "Software Engineering", "Prof. Wang"));
// Display all students
sys.displayAll();
return 0;
}
Output:
Added successfully!
No student information available
-------------------
Run result:
Added successfully!
Added successfully!
Student ID: 001
Name: Zhang San
Age: 20
Major: Computer Science
Year: 3
Type: Undergraduate
-------------------
Student ID: 002
Name: Li Si
Age: 24
Major: Software Engineering
Advisor: Prof. Wang
Type: Graduate Student
-------------------
3. Complete Version Implementation
(1) 3.1 Adding More Features
Now, let's add more features to the system:
| Feature | Implementation |
|---|---|
| Delete student | Delete by student ID |
| Modify info | Find by student ID, then modify |
| Grade management | Add a Grade class |
| Save to file | Use ofstream to write file |
| Load from file | Use ifstream to read file |
(2) 3.2 Grade Class Design
// Grade class
### ▶ Example 2: Object-Oriented Programming Demo (Difficulty ⭐)
class Grade {
private:
std::string courseId; // Course ID
std::string courseName; // Course name
double score; // 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 << " points" << std::endl;
}
double getScore() const { return score; }
};
// Add grade-related methods to the Student class
class Student {
// ... other members ...
std::vector<Grade> grades; // Grade list
public:
void addGrade(const Grade& grade) {
grades.push_back(grade);
}
void displayGrades() const {
if (grades.empty()) {
std::cout << "No grades available" << std::endl;
return;
}
std::cout << name << "'s grades:" << 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 File Operations
Save student information to file:
void saveToFile(const std::string& filename) {
std::ofstream file(filename);
if (!file) {
std::cout << "Cannot open file" << std::endl;
return;
}
for (auto s : students) {
file << s->getId() << ","
<< s->getName() << ","
<< s->getAge() << std::endl;
}
file.close();
std::cout << "Saved successfully!" << std::endl;
}
Load from file:
void loadFromFile(const std::string& filename) {
std::ifstream file(filename);
if (!file) {
std::cout << "Cannot open file" << 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);
// Create student object (simplified; should distinguish undergraduate/graduate)
students.push_back(new UndergraduateStudent(id, name, age, "Unknown", 1));
}
file.close();
std::cout << "Loaded successfully!" << std::endl;
}
4. Menu Interface Implementation
(1) 4.1 Main Menu
void showMenu() {
std::cout << "\n===== Student Management System =====" << std::endl;
std::cout << "1. Add student" << std::endl;
std::cout << "2. Find student" << std::endl;
std::cout << "3. Display all students" << std::endl;
std::cout << "4. Enter grades" << std::endl;
std::cout << "5. Save data" << std::endl;
std::cout << "6. Load data" << std::endl;
std::cout << "0. Exit" << std::endl;
std::cout << "=====================================" << std::endl;
std::cout << "Choose: ";
}
int main() {
ManagementSystem sys;
int choice;
do {
showMenu();
std::cin >> choice;
switch (choice) {
case 1:
// Add student
break;
case 2:
// Find student
break;
case 3:
sys.displayAll();
break;
case 0:
std::cout << "Goodbye!" << std::endl;
break;
default:
std::cout << "Invalid choice" << std::endl;
}
} while (choice != 0);
return 0;
}
5. Project Extension Suggestions
(1) 5.1 Feature Extensions
| Feature | Difficulty | Description |
|---|---|---|
| Grade Statistics | ⭐⭐ | Calculate averages, rankings |
| Data Validation | ⭐⭐ | Check input validity |
| Exception Handling | ⭐⭐⭐ | Use try-catch for errors |
| GUI | ⭐⭐⭐⭐ | Use Qt or wxWidgets |
| Database | ⭐⭐⭐⭐ | Use SQLite or MySQL |
(2) 5.2 Code Optimization
| Optimization | Description |
|---|---|
| Smart Pointers | Use unique_ptr for memory management |
| Const Correctness | Use const wherever possible |
| Exception Handling | Use try-catch for file errors |
| Operator Overloading | Overload << for output |
▶ Example 3: Simple Class Inheritance Hierarchy (Difficulty ⭐)
#include <iostream>
#include <string>
class Animal {
public:
std::string name;
Animal(const std::string& n) : name(n) {}
virtual void speak() const = 0;
virtual ~Animal() {}
};
class Dog : public Animal {
public:
Dog(const std::string& n) : Animal(n) {}
void speak() const override {
std::cout << name << ": Woof!" << std::endl;
}
};
class Cat : public Animal {
public:
Cat(const std::string& n) : Animal(n) {}
void speak() const override {
std::cout << name << ": Meow!" << std::endl;
}
};
int main() {
Dog dog("Wangcai");
Cat cat("Mimi");
dog.speak();
cat.speak();
return 0;
}
Output:
: Woof!
: Meow!
❓ FAQ
Q: Why use pointers to store students? A: To achieve polymorphism. Using
vector<Student>would cause object slicing, so only base class methods could be called.
Q: What about memory leaks? A: Use smart pointers (
unique_ptr) to automatically manage memory — no need for manualdelete.
Q: How should I design the file format? A: You can use: - CSV format: simple, but limited functionality - JSON format: powerful, requires third-party library - Binary format: efficient, but not human-readable
wstring + wcout - Or use a third-party library (e.g., iconv) - Or only store pinyin/English📖 Summary
| Knowledge Point | Key Takeaway |
|---|---|
| Class Design | Base class + derived class, reflecting inheritance |
| Polymorphism | Use pointers/references to call virtual functions |
| File Operations | ofstream for writing, ifstream for reading |
| Memory Management | Destructor frees memory (or use smart pointers) |
| Menu Interface | do-while + switch |
Study suggestions:
- This project is a comprehensive exercise — make sure to type it out yourself
- Implement basic features first, then gradually extend
- When encountering problems, search for solutions first — develop problem-solving skills
📝 Exercises
-
Basic (Difficulty ⭐): Define a Book class (title, author, price), create 3 objects and output their properties.
-
Intermediate (Difficulty ⭐⭐): Implement an inheritance hierarchy: Shape (base class, pure virtual function area()) → Circle and Rectangle derived classes. Call area() polymorphically.
-
Challenge (Difficulty ⭐⭐⭐): Implement an "Observer pattern": Subject class maintains an Observer list, Observer is an abstract base class. When Subject state changes, notify all Observers.
- This section covered the core concepts of OOP practice
- With mastery, you can write related C++ programs
Phase 5 complete! Next: Phase 6 (Advanced STL)