Skip to content

Instantly share code, notes, and snippets.

@alzobnin
Last active October 20, 2020 09:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alzobnin/87ebcc4978c2bfe6d9aa8d66b9ef7e2b to your computer and use it in GitHub Desktop.
Save alzobnin/87ebcc4978c2bfe6d9aa8d66b9ef7e2b to your computer and use it in GitHub Desktop.

Разбор задачи «Рациональное число»

Условие

Напишите класс Rational (рациональное число). Конструктор класса должен получать на вход два числа типа int (числитель и знаменатель). Считайте, что по умолчанию числитель равен 0, а знаменатель — 1. Переопределите бинарные операторы сложения, вычитания, умножения и деления (работающие в том числе и с аргументами типа int), унарные плюс и минус. Предусмотрите функции-члены numerator и denominator для получения числителя и знаменателя несократимого представления этой дроби (знаменатель должен быть положительным). Переопределите также операторы +=, -=, *= и /=. Не забудьте определить операторы == и !=. Реализуйте также префиксный и постфиксный операторы ++ и --.

Решение

Будем хранить рацинальное число в виде несократимой дроби. Для этого нам потребуется функция gcd, которая появилась в C++17. Впрочем, можно написать самостоятельно её реализацию с помощью алгоритма Евклида:

int gcd(int a, int b) {
    if (b == 0)
        return a;
    return gcd(b, a % b);
}

Мы введём вспомогательную приватную функцию reduce, которая будет сокращать дробь и гарантировать, что знаменатель неотрицателен. Эту функцию мы будем вызывать всякий раз, когда при вычислениях дробь могла стать сократимой.

#include <numeric>

class Rational {
private:
    int num;
    int denom;

    void reduce() {
        int d = std::gcd(num, denom);
        num /= d;
        denom /= d;
        if (denom < 0) {
            num *= -1;
            denom *= -1;
        }
    }

public:
    Rational(int num_ = 0, int denom_ = 1) : num(num_), denom(denom_) {
        reduce();
    }

    int numerator() const {
        return num;
    }

    int denominator() const {
        return denom;
    }

    // унарный плюс (формально ничего не меняет)
    Rational operator+() const {
        return {num, denom};
    }

    // унарный минус
    Rational operator-() const {
        return {-num, denom};
    }

    // rhs - сокращение от right hand side (правый аргумент бинарного оператора)
    Rational& operator+=(const Rational& rhs) {
        num = num * rhs.denom + rhs.num * denom;
        denom *= rhs.denom;
        reduce();
        return *this;
    }

    Rational& operator-=(const Rational& rhs) {
        num = num * rhs.denom - rhs.num * denom;
        denom *= rhs.denom;
        reduce();
        return *this;
    }

    Rational& operator*=(const Rational& rhs) {
        num *= rhs.num;
        denom *= rhs.denom;
        reduce();
        return *this;
    }

    Rational& operator/=(const Rational& rhs) {
        // сохраняем старый числитель, чтобы корректно работало выражение q /= q.
        int tmp = rhs.num;
        num *= rhs.denom;
        denom *= tmp;
        reduce();
        return *this;
    }

    // префиксный ++
    Rational& operator++() {
        num += denom;
        // здесь нет необходимости вызывать reduce(), так как дробь остаётся несократимой
        return *this;
    }

    Rational& operator--() {
        num -= denom;
        return *this;
    }

    // постфиксный ++ формально определяется с дополнительным игнорируемым параметром типа int
    Rational operator++(int) {
        Rational tmp = *this;
        ++*this;
        return tmp;
    }

    Rational operator--(int) {
        Rational tmp = *this;
        --*this;
        return tmp;
    }
};

Rational operator+(const Rational& lhs, const Rational& rhs) {
    Rational result = lhs;
    result += rhs;
    return result;
}

Rational operator-(const Rational& lhs, const Rational& rhs) {
    Rational result = lhs;
    result -= rhs;
    return result;
}

Rational operator*(const Rational& lhs, const Rational& rhs) {
    Rational result = lhs;
    result *= rhs;
    return result;
}

Rational operator/(const Rational& lhs, const Rational& rhs) {
    Rational result = lhs;
    result /= rhs;
    return result;
}

bool operator==(const Rational& lhs, const Rational& rhs) {
    return lhs.numerator() == rhs.numerator() && lhs.denominator() == rhs.denominator();
}

bool operator!=(const Rational& lhs, const Rational& rhs) {
    return !(lhs == rhs);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment