Menu
Coddy logo textTech

Перегрузка арифметических операторов

Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy — урок 41 из 104.

Арифметические операторы, такие как +, -, * и /, часто перегружаются, чтобы пользовательские классы вели себя подобно встроенным числовым типам. Эти операторы обычно возвращают новый объект, вместо того чтобы изменять операнды.

Вот класс Fraction с арифметическими операторами:

class Fraction {
    int numerator, denominator;
    
public:
    Fraction(int n, int d) : numerator(n), denominator(d) {}
    
    Fraction operator+(const Fraction& other) const {
        return Fraction(
            numerator * other.denominator + other.numerator * denominator,
            denominator * other.denominator
        );
    }
    
    Fraction operator*(const Fraction& other) const {
        return Fraction(numerator * other.numerator, 
                        denominator * other.denominator);
    }
};

Обратите внимание, что оба оператора помечены как const, поскольку они не изменяют ни один из операндов. Они создают и возвращают новый Fraction с результатом.

Для операторов составного присваивания, таких как +=, шаблон отличается. Они изменяют левый операнд и возвращают ссылку на него:

Fraction& operator+=(const Fraction& other) {
    numerator = numerator * other.denominator + other.numerator * denominator;
    denominator = denominator * other.denominator;
    return *this;
}

Распространенной практикой является реализация базового оператора через составное присваивание, что позволяет сократить дублирование кода:

Fraction operator+(const Fraction& other) const {
    Fraction result = *this;    // Копируем этот объект
    result += other;            // Используем составное присваивание
    return result;
}

Этот подход гарантирует согласованность обоих операторов и упрощает обслуживание.

challenge icon

Задание

Легко

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

Для организации кода вы создадите два файла:

  • Money.h: Определите класс Money, который хранит сумму в центах (как int), чтобы избежать проблем с точностью чисел с плавающей запятой. Ваш класс должен поддерживать:
    • Конструктор, принимающий доллары и центы как отдельные целые числа
    • Геттер getDollars(), который возвращает долларовую часть
    • Геттер getCents(), который возвращает часть в центах (0-99)
    • Перегруженные операторы + и -, которые возвращают новый объект Money
    • Перегруженные операторы составного присваивания += и -=, которые изменяют текущий объект и возвращают ссылку на него
    • Метод display(), который выводит денежную сумму в формате $X.YY (всегда две цифры для центов)

    Реализуйте базовые операторы (+ и -) через операторы составного присваивания, чтобы уменьшить дублирование кода, как показано в уроке. Все арифметические операторы должны быть помечены как const там, где это уместно.

  • main.cpp: Считайте четыре целых числа из входных данных, представляющих две денежные суммы (dollars1, cents1, dollars2, cents2 на отдельных строках). Создайте два объекта Money, а затем продемонстрируйте работу всех ваших операторов:
    • Выведите "Amount 1: ", а затем первую сумму
    • Выведите "Amount 2: ", а затем вторую сумму
    • Сложите две суммы и выведите "Sum: ", а затем результат
    • Вычтите вторую сумму из первой и выведите "Difference: ", а затем результат
    • Используйте +=, чтобы добавить вторую сумму к первой, затем выведите "After +=: " и измененную первую сумму

Для метода display() используйте std::setw(2) и std::setfill('0') из библиотеки <iomanip>, чтобы центы всегда отображались двумя цифрами (например, $5.07, а не $5.7).

Преобразуйте входные строки в целые числа с помощью std::stoi(). Не забудьте про include guards (защиту от повторного включения) в вашем заголовочном файле.

Шпаргалка

Арифметические операторы, такие как +, -, * и /, могут быть перегружены, чтобы пользовательские классы вели себя как встроенные числовые типы. Эти операторы обычно возвращают новый объект, а не изменяют операнды.

Базовые арифметические операторы должны быть помечены как const, так как они не изменяют ни один из операндов:

Fraction operator+(const Fraction& other) const {
    return Fraction(
        numerator * other.denominator + other.numerator * denominator,
        denominator * other.denominator
    );
}

Составные операторы присваивания, такие как += и -=, изменяют левый операнд и возвращают ссылку на него:

Fraction& operator+=(const Fraction& other) {
    numerator = numerator * other.denominator + other.numerator * denominator;
    denominator = denominator * other.denominator;
    return *this;
}

Распространенной практикой является реализация базовых операторов через составные операторы присваивания для уменьшения дублирования кода:

Fraction operator+(const Fraction& other) const {
    Fraction result = *this;    // Копируем этот объект
    result += other;            // Используем составное присваивание
    return result;
}

Попробуйте сами

#include <iostream>
#include <string>
#include "Money.h"

using namespace std;

int main() {
    // Считать входные данные для двух денежных сумм
    string line;
    
    getline(cin, line);
    int dollars1 = stoi(line);
    
    getline(cin, line);
    int cents1 = stoi(line);
    
    getline(cin, line);
    int dollars2 = stoi(line);
    
    getline(cin, line);
    int cents2 = stoi(line);
    
    // TODO: Создать два объекта Money с входными значениями
    
    // TODO: Вывести "Amount 1: ", а затем первую отображаемую сумму
    
    // TODO: Вывести "Amount 2: ", а затем вторую отображаемую сумму
    
    // TODO: Сложить две суммы и вывести "Sum: ", а затем результат
    
    // TODO: Вычесть вторую сумму из первой и вывести "Difference: ", а затем результат
    
    // TODO: Использовать +=, чтобы прибавить вторую сумму к первой, затем вывести "After +=: ", а затем измененную первую сумму
    
    return 0;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование