Итоги — Система учета студентов
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy — урок 39 из 104.
Пришло время применить ваши знания об инкапсуляции на практике! В этом испытании вы создадите Систему учета студентов, которая демонстрирует правильное использование спецификаторов доступа, сокрытие информации и вложенные классы.
Ваша система будет управлять данными студентов, используя следующие принципы инкапсуляции:
- Закрытые члены данных (Private data members) для защиты конфиденциальной информации, такой как оценки и идентификаторы студентов
- Открытый интерфейс (Public interface) с геттерами и контролируемыми сеттерами
- Вложенный класс для представления оценок по отдельным курсам, что позволяет скрыть детали реализации внутри класса студента
Вот структура, которую вам предстоит реализовать:
class Student {
public:
class Grade { // Вложенный класс для оценок по курсам
// ...
};
// Открытый интерфейс для взаимодействия с данными студента
private:
std::string name;
int studentId;
// Коллекция оценок
};Это испытание объединяет все темы из этой главы: выбор подходящих уровней доступа, сокрытие деталей реализации за чистым интерфейсом и использование вложенных классов для организации связанных типов. Тщательно продумайте, что должно быть доступно, а что должно оставаться скрытым.
Задание
ЛегкоДавайте создадим систему учета студентов, которая объединит все, что вы узнали об инкапсуляции. Вы создадите систему, в которой данные студентов надежно защищены, оценки управляются через вложенный класс, а все взаимодействие происходит через чистый публичный интерфейс.
Для организации кода вы создадите два файла:
Student.h: Определите классStudent, который демонстрирует правильную инкапсуляцию с вложенным классомGrade.Ваш вложенный класс
Grade(объявленный в публичной секцииStudent) должен хранить название курса (string) и оценку (double). Создайте для него конструктор, принимающий оба значения, и предоставьте методыgetCourseName()иgetScore()для доступа к данным. КлассGradeхранит свои данные в приватной секции — даже будучи вложенным вStudent, он сохраняет собственную инкапсуляцию.Класс
Studentдолжен иметь приватные члены для имени студента (string), идентификатора студента (int) и коллекции указателей наGrade. Реализуйте:- Конструктор, принимающий имя и идентификатор студента
- Геттеры
getName()иgetStudentId()(оба const) - Метод
addGrade(const std::string& course, double score), который создает новый объектGradeи добавляет его в запись студента (добавляйте только если оценка находится в диапазоне от 0 до 100 включительно) - Метод
getAverageGrade()(const), который вычисляет и возвращает средний балл всех оценок или 0.0, если оценок нет - Метод
displayRecord()(const), который показывает полную запись студента
Не забудьте очистить динамически выделенную память для оценок в деструкторе и включить соответствующие guard-заголовки.
main.cpp: Считайте имя студента, идентификатор студента и три оценки за курсы из входных данных. Формат ввода будет состоять из пяти строк: имя, ID, затем три строки, каждая из которых содержит название курса и оценку, разделенные пробелом (например,Math 95.5).Создайте объект
Studentс именем и ID, добавьте все три оценки, а затем отобразите полную запись.Формат вывода для
displayRecord()должен быть следующим:Student: <name> (ID: <id>) Grades: <course1>: <score1> <course2>: <score2> <course3>: <score3> Average: <average>
Форматируйте все оценки и средний балл с одним десятичным знаком, используя std::fixed и std::setprecision(1) из библиотеки <iomanip>. Преобразуйте ID с помощью std::stoi(), а оценки — с помощью std::stod().
Эта задача объединяет приватные члены данных, публичные геттеры, контролируемые сеттеры с валидацией и вложенный класс — все это работает вместе для создания хорошо инкапсулированной системы учета студентов.
Шпаргалка
Инкапсуляция объединяет спецификаторы доступа, сокрытие информации и вложенные классы для защиты данных и организации кода.
Спецификаторы доступа для защиты данных
Используйте private для конфиденциальных членов данных и public для интерфейса:
class Student {
public:
// Публичные методы интерфейса
private:
std::string name;
int studentId;
// Другие приватные данные
};Вложенные классы
Вложенные классы организуют связанные типы внутри класса, сохраняя собственную инкапсуляцию:
class Student {
public:
class Grade { // Вложенный класс
private:
std::string courseName;
double score;
public:
Grade(const std::string& course, double s)
: courseName(course), score(s) {}
std::string getCourseName() const { return courseName; }
double getScore() const { return score; }
};
private:
std::vector<Grade*> grades;
};Контролируемый доступ с валидацией
Методы могут проверять данные перед изменением приватных членов:
void addGrade(const std::string& course, double score) {
if (score >= 0 && score <= 100) {
grades.push_back(new Grade(course, score));
}
}Константные методы
Помечайте методы, которые не изменяют данные, как const:
std::string getName() const { return name; }
double getAverageGrade() const {
// Вычислить и вернуть среднее значение
}Управление памятью
Освобождайте динамически выделенную память для объектов в деструкторе:
~Student() {
for (Grade* grade : grades) {
delete grade;
}
}Попробуйте сами
#include <iostream>
#include <string>
#include <sstream>
#include "Student.h"
int main() {
// Считать имя студента
std::string name;
std::getline(std::cin, name);
// Считать ID студента
std::string idStr;
std::getline(std::cin, idStr);
int studentId = std::stoi(idStr);
// Считать оценки по трем курсам (формат: "CourseName Score")
std::string line1, line2, line3;
std::getline(std::cin, line1);
std::getline(std::cin, line2);
std::getline(std::cin, line3);
// TODO: Разобрать каждую строку, чтобы извлечь название курса и оценку
// Используйте std::stod() для преобразования строк с оценками в double
// TODO: Создать объект Student с именем и ID
// TODO: Добавить все три оценки студенту
// TODO: Вывести полную запись о студенте
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнер