6

Основы программирования на C++: Классы

Прежде чем читать мою статью - реши для себя, зачем ты это делаешь. Даже если ты просто нормальный человек, лишним не будет.

Если вы настоящий профессионал программирования, то зачем вы тут сидите и читайте статью на пикабу? Если вы ради интереса зашли почитать, то претензий ноль, но если вы просто захотели задушить нового пользователя Пикабу минусами, то немедленно покиньте статью, вам однозначно интересно не будет.

Здравствуйте, мои маленькие любители программирования!

Классы и их особенности

Классы в программировании похожи на структуры: они представляют собой пользовательские типы данных, которые содержат поля. Синтаксически ключевые слова struct и class взаимозаменяемы, однако между ними есть важное различие. В struct поля по умолчанию являются публичными, а в class — приватными. Мы будем использовать эти термины для обозначения разных подходов к организации данных.

  • Структуры (struct) используются, когда нам не требуется сложная логика для работы с данными. Это просто набор полей без каких-либо ограничений. Пример: структура Point, которая хранит координаты.

  • Классы (class) применяются, когда необходимо контролировать данные, обеспечивать их корректность и выполнять определённые действия при инициализации или изменении. Например, класс Time гарантирует, что время всегда находится в допустимых пределах.

Классы не только задают тип данных, но и определяют их поведение. Переменные такого типа называются объектами.


Объявление класса

Рассмотрим пример простой структуры, которая хранит время в часах, минутах и секундах:

struct Time {

int hours = 0;

int minutes = 0;

int seconds = 0;

};

Эта структура удобна, но она не проверяет корректность значений. Например, можно присвоить часам значение 42, а минутам — -5. Чтобы избежать таких ошибок, объявим класс Time.

В классе поля будут находиться в приватной области, доступ к которой ограничен. Публичная часть будет содержать конструктор для инициализации и методы для чтения значений:

class Time {

private:

int hours;

int minutes;

int seconds;

public:

Time(int h, int m, int s); // Конструктор

int GetHours() const; // Метод для получения часов

int GetMinutes() const; // Метод для получения минут

int GetSeconds() const; // Метод для получения секунд

};

Здесь конструктор отвечает за начальную инициализацию объекта, а методы GetHours, GetMinutes и GetSeconds объявлены как константные (с пометкой const), что означает их невозможность изменять состояние объекта.


Реализация функций

Теперь реализуем объявленные функции. Для этого можно написать их тела внутри класса или отдельно. При внешнем определении используется префикс с именем класса и двоеточием:

Time::Time(int h, int m, int s) {

if (s < 0 || s > 59) {

// Обработка ошибочных секунд

}

if (m < 0 || m > 59) {

// Обработка ошибочных минут

}

if (h < 0 || h > 23) {

// Обработка ошибочных часов

}

hours = h;

minutes = m;

seconds = s;

}

int Time::GetHours() const {

return hours;

}

int Time::GetMinutes() const {

return minutes;

}

int Time::GetSeconds() const {

return seconds;

}

Обратите внимание, что каждая функция класса работает с текущим объектом, который передаётся неявно. Этот объект доступен через указатель this.


Обработка ошибок в конструкторе

Если конструктор получает некорректные значения, он может либо сгенерировать исключение, либо скорректировать данные. Рассмотрим второй вариант:

Time::Time(int h, int m, int s) {

m += s / 60;

s %= 60;

if (s < 0) {

m -= 1;

s += 60;

}

h += m / 60;

m %= 60;

if (m < 0) {

h -= 1;

m += 60;

}

h %= 24;

if (h < 0) {

h += 24;

}

hours = h;

minutes = m;

seconds = s;

}

Теперь объекты класса Time всегда будут содержать корректное время.


Перегрузка конструкторов

Можно добавить несколько конструкторов для удобства. Например, конструктор без параметров и конструктор, принимающий общее количество секунд:

class Time {

private:

int hours = 0;

int minutes = 0;

int seconds = 0;

public:

Time() = default; // Конструктор по умолчанию

Time(int h, int m, int s); // Основной конструктор

Time(int s): Time(0, 0, s) {} // Делегирующий конструктор

};


Изменение состояния объекта

Чтобы позволить изменять объект после создания, добавим метод AddSeconds:

void AddSeconds(int s) {

seconds += s;

Normalize(); // Корректировка времени

}

Этот метод нельзя вызвать для константного объекта, так как он изменяет состояние.


Перегрузка операторов

Для удобства можно перегрузить операторы, такие как += и +. Например:

Time& operator += (int s) {

seconds += s;

Normalize();

return *this;

}

Time operator + (int s) const {

return Time(hours, minutes, seconds + s);

}

Теперь можно писать t += 40 или t + 20.


Ввод и вывод

Операторы << и >> можно перегрузить для работы с потоками:

std::ostream& operator << (std::ostream& out, const Time& t) {

out << t.GetHours() << ":" << t.GetMinutes() << ":" << t.GetSeconds();

return out;

}

std::istream& operator >> (std::istream& in, Time& t) {

int h, m, s;

char dummy;

in >> h >> dummy >> m >> dummy >> s;

t = Time(h, m, s);

return in;

}

Теперь можно легко читать и выводить время.


Модификация реализации

Интерфейс класса остаётся неизменным, даже если внутренняя реализация меняется. Например, вместо трёх полей (hours, minutes, seconds) можно хранить только одно поле totalSeconds:

class Time {

private:

int totalSeconds;

void Normalize() {

const int secondsInDay = 24 * 60 * 60;

totalSeconds %= secondsInDay;

if (totalSeconds < 0) {

totalSeconds += secondsInDay;

}

}

public:

Time(int h, int m, int s) {

totalSeconds = h * 60 * 60 + m * 60 + s;

Normalize();

}

int GetHours() const {

return totalSeconds / (60 * 60);

}

int GetMinutes() const {

return (totalSeconds / 60) % 60;

}

int GetSeconds() const {

return totalSeconds % 60;

}

};

Это упрощает код, сохраняя совместимость с существующими программами.


Таким образом, классы позволяют создавать надёжные и гибкие типы данных, которые легко модифицировать и расширять.

Задание 1: Класс Point

Создайте класс Point, который представляет точку в двумерном пространстве с координатами (x, y). Реализуйте следующие функциональности:

  1. Приватные поля x и y.

  2. Конструкторы :

    • Конструктор без параметров (инициализирует координаты нулями).

    • Конструктор с двумя параметрами для установки значений x и y.

  3. Методы доступа :

    • GetX() и GetY() для получения значений координат.

    • SetX(int x) и SetY(int y) для изменения значений координат.

  4. Перегрузка операторов :

    • Перегрузите оператор + для сложения двух точек (координаты складываются поэлементно).

    • Перегрузите оператор - для вычитания двух точек.

  5. Дополнительно : добавьте метод DistanceTo(const Point& other), который вычисляет расстояние между текущей точкой и другой точкой.


Задание 2: Класс Fraction

Реализуйте класс Fraction, представляющий дробь вида a/b (где a — числитель, b — знаменатель). Добавьте следующие возможности:

  1. Приватные поля numerator и denominator.

  2. Конструкторы :

    • Конструктор с двумя параметрами для инициализации числителя и знаменателя.

    • Конструктор с одним параметром (знаменатель по умолчанию равен 1).

  3. Методы доступа :

    • GetNumerator() и GetDenominator().

    • Метод Simplify(), который сокращает дробь до несократимой формы (например, 4/81/2).

  4. Перегрузка операторов :

    • Перегрузите операторы +, -, *, / для выполнения арифметических операций с дробями.

    • Перегрузите операторы сравнения (==, !=, <, >, <=, >=).

  5. Дополнительно : добавьте метод ToDouble(), который возвращает значение дроби как число типа double.


Задание 3: Класс BankAccount

Создайте класс BankAccount, моделирующий банковский счёт. Реализуйте следующие функциональности:

  1. Приватные поля :

    • balance (баланс счёта).

    • accountNumber (номер счёта, строка или целое число).

  2. Конструкторы :

    • Конструктор с начальным балансом и номером счёта.

    • Конструктор без параметров (начальный баланс = 0, номер счёта генерируется автоматически).

  3. Методы :

    • Deposit(double amount) для пополнения счёта.

    • Withdraw(double amount) для снятия денег (с проверкой на достаточность средств).

    • GetBalance() для получения текущего баланса.

  4. Перегрузка операторов :

    • Перегрузите оператор += для пополнения счёта.

    • Перегрузите оператор -= для снятия денег.

  5. Дополнительно : добавьте метод Transfer(BankAccount& other, double amount), который переводит деньги с одного счёта на другой.


Задание 4: Класс Date

Реализуйте класс Date, представляющий дату в формате день-месяц-год. Добавьте следующие возможности:

  1. Приватные поля :

    • day, month, year.

  2. Конструкторы :

    • Конструктор с тремя параметрами для инициализации дня, месяца и года.

    • Конструктор без параметров (текущая дата по умолчанию).

  3. Методы :

    • GetDay(), GetMonth(), GetYear() для получения значений.

    • SetDate(int day, int month, int year) для изменения даты.

    • IsValid() для проверки корректности даты (например, 31 февраля недопустимо).

  4. Перегрузка операторов :

    • Перегрузите оператор ++ для увеличения даты на один день.

    • Перегрузите оператор -- для уменьшения даты на один день.

  5. Дополнительно : добавьте метод DaysBetween(const Date& other), который возвращает количество дней между текущей датой и другой датой.


Задание 5: Класс Matrix

Создайте класс Matrix, представляющий матрицу чисел. Реализуйте следующие функциональности:

  1. Приватные поля :

    • Двумерный массив для хранения элементов матрицы.

    • Размеры матрицы (rows, columns).

  2. Конструкторы :

    • Конструктор с размерами матрицы и начальными значениями элементов.

    • Конструктор без параметров (матрица размером 1x1 с нулевым элементом).

  3. Методы :

    • GetElement(int row, int col) и SetElement(int row, int col, int value) для доступа к элементам.

    • Print() для вывода матрицы на экран.

  4. Перегрузка операторов :

    • Перегрузите оператор + для сложения двух матриц.

    • Перегрузите оператор * для умножения матриц (проверьте совместимость размеров).

  5. Дополнительно : добавьте метод Transpose(), который возвращает транспонированную матрицу.


Задание 6: Класс Student

Создайте класс Student, представляющий студента с информацией о его имени, возрасте и оценках. Реализуйте следующие функциональности:

  1. Приватные поля :

    • name (имя студента).

    • age (возраст студента).

    • grades (массив оценок).

  2. Конструкторы :

    • Конструктор с именем, возрастом и массивом оценок.

    • Конструктор только с именем (остальные поля по умолчанию).

  3. Методы :

    • GetName(), GetAge(), GetGrades() для получения данных.

    • AddGrade(int grade) для добавления новой оценки.

    • AverageGrade() для вычисления средней оценки.

  4. Перегрузка операторов :

    • Перегрузите оператор << для вывода информации о студенте в поток.

    • Перегрузите оператор >> для ввода данных о студенте из потока.

  5. Дополнительно : добавьте метод IsExcellentStudent(), который возвращает true, если средняя оценка >= 4.5.


Задание 7: Класс String

Реализуйте свой собственный класс String, аналогичный стандартному std::string. Добавьте следующие возможности:

  1. Приватные поля :

    • Указатель на массив символов (char*).

    • Длина строки.

  2. Конструкторы :

    • Конструктор с C-style строкой (например, "Hello").

    • Конструктор без параметров (пустая строка).

  3. Методы :

    • Length() для получения длины строки.

    • Concat(const String& other) для конкатенации строк.

    • Substring(int start, int length) для получения подстроки.

  4. Перегрузка операторов :

    • Перегрузите оператор + для конкатенации строк.

    • Перегрузите оператор == для сравнения строк.

  5. Дополнительно : реализуйте метод Find(char c), который возвращает индекс первого вхождения символа в строку.

Лига программистов

2.1K постов11.9K подписчика

Правила сообщества

- Будьте взаимовежливы, аргументируйте критику

- Приветствуются любые посты по тематике программирования

- Если ваш пост содержит ссылки на внешние ресурсы - он должен быть самодостаточным. Вариации на тему "далее читайте в моей телеге" будут удаляться из сообщества