Перегрузка чисто виртуальных операторов - PullRequest
2 голосов
/ 06 июня 2019

У меня есть абстрактный класс Number с 4 чистыми виртуальными операторами (+, -, /, *).Я хочу сделать два производных класса Integer и Real и переопределить там эти операторы.Я не совсем понимаю, как объявлять операторов.Я не нашел в сети примеров, похожих на мою ситуацию.

Мои вопросы: В абстрактном базовом классе Number мои операторы должны возвращать ссылку Number& или указатель Number*, поскольку я могу 'не вернуть сам абстрактный класс.Но что я должен передать в качестве аргументов?Number& или Number* тоже, но мне нужно хранить номер в классе, должен ли я иметь в своем базовом классе что-то вроде void* num?Давайте представим, у меня есть

class Number {
    ...
    virtual Number& operator+(const Number&) = 0;
};

Как мне переопределить этот оператор в производном классе Integer?

1 Ответ

4 голосов
/ 06 июня 2019

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

Ну, вы можете научитьсяо двух вещах из этого:

  1. Как работают виртуальные функции в целом (а пользовательский оператор - это не что иное, как обычная функция, отличается только синтаксис вызова).
  2. Виртуальные функции не используются.Святой Грааль не может решить что-нибудь .

Проблема в том, что классы Integer и Real, скорее всего, имеют различное внутреннее представление - так что вы не будетевозможность сложения / умножения / ... не зная о конкретном типе, который вы получили в качестве второго операнда.Кроме того, довольно непонятно, каким должен быть возвращаемый тип смешанных типов операндов.

Мне не нужно atm для добавления real + int, только real + real, int + int * int

Хорошо, хорошо, мы можем уловить это:

class Number
{
public:
    virtual ~Number() { } // in any case, a virtual base class should have a virtual destructor!

    // fine so far:
    virtual Number& operator+(Number const&) const = 0;
    // we are not going to change the          ^
    // own instance, so we'd rather have this operator const
    // (in contrast to operator+=)
};

class Integer : public Number
{
public:
    Integer& operator+(Number const& other) const override
    // ^ co-variant return type, so that's fine
    {
        // at very first, we make sure that we actually received
        // an instance of type Integer
        auto o = dynamic_cast<Integer const*>(&other);
        if(!o)
            // if not, throwing an exception is a good candidate
            // for error handling...
            throw std::invalid_argument("argument must be of type Integer");

        // now as we know it IS an Integer:
        return Integer(this->value + o->value); // or whatever...
        // OOPS - the returned object gets lost when the function is left...
    }
};

Если вы хотите иметь возможность добавлять Real s, тогда у вас будет другой тип приведения.Если предположить, что Integer + Real приведет к Real, вам придется изменить тип возвращаемого значения на Number, однако.

Тем не менее, есть большая проблема: возвращаемый объект уничтожается, как толькофункция оставлена, поэтому возвращаемая ссылка болтается.

Нам придется исправить это каким-то образом.Однако ссылки не подходят, поэтому мы можем выбрать интеллектуальный указатель:

class Number
{
    virtual std::unique_ptr<Number> operator+(Number const&) const = 0;
};

class Integer : public Number
{
    std::unique_ptr<Number> operator+(Number const& other) const override
    //                 ^
    // unfortunately, now, as ordinary struct, no co-variance possible any more
    {
        return std::make_unique<Integer>(this->value + o->value);
    }
};

Эта проблема еще раз показывает, насколько неподходящим является выбранный подход на самом деле ...

...