Перегрузка арифметических операторов
Часть раздела Объектно-ориентированное программирование путешествия по 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;
}Этот подход гарантирует согласованность обоих операторов и упрощает обслуживание.
Задание
ЛегкоДавайте создадим класс 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;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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Итоги — Обобщенный контейнер