Перегрузка арифметических операторов
Часть раздела Объектно-ориентированное программирование путешествия по 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 с результатом.
Для compound assignment операторов, таких как +=, шаблон отличается. Они изменяют левый операнд и возвращают ссылку на него:
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, который хранит сумму в cents (какint), чтобы избежать проблем с точностью чисел с плавающей запятой. Ваш класс должен поддерживать:- конструктор, принимающий dollars и cents как отдельные целые числа
- геттер
getDollars(), возвращающий долларовую часть - геттер
getCents(), возвращающий часть в cents (0–99) - перегруженные операторы
+и-, возвращающие новый объектMoney - перегруженные составные операторы присваивания
+=и-=, изменяющие текущий объект и возвращающие ссылку на него - метод
display(), который выводит деньги в формате$X.YY(для cents всегда используются две цифры)
Реализуйте базовые операторы (
+и-) через составные операторы присваивания, чтобы сократить duplication, как показано в уроке. Все арифметические операторы должны быть помечены какconst, где это уместно.main.cpp: считайте из input четыре целых числа, представляющих две денежные суммы (dollars1, cents1, dollars2, cents2 в отдельных строках). Создайте два объектаMoney, затем продемонстрируйте работу всех операторов:- выведите
"Amount 1: ", followed by первую отображённую сумму - выведите
"Amount 2: ", followed by вторую отображённую сумму - сложите две суммы и выведите
"Sum: ", followed by результат - вычтите вторую сумму из первой и выведите
"Difference: ", followed by результат - используйте
+=, чтобы прибавить вторую сумму к первой, затем выведите"After +=: ", followed by изменённую первую сумму
- выведите
Для метода display() используйте std::setw(2) и std::setfill('0') из <iomanip>, чтобы cents всегда отображались двумя цифрами (например, $5.07, а не $5.7).
Преобразуйте строки input в целые числа с помощью std::stoi(). Не забудьте добавить защитные конструкции заголовка в файл заголовка.
Попробуйте сами
#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Итоги — Обобщенный контейнерПотренируйтесь самостоятельно: Онлайн-компилятор C++