Как переопределить оператор полиморфизма

Может ли кто-нибудь объяснить мне, что я делаю неправильно здесь?

struct X {
    int x{};

    explicit X(int x) : x(x) {}

    virtual X &operator++() = 0;
};

struct OK : X {
    int ok{};

    explicit OK(int ok) : X(ok), ok(ok) {}

    X &operator++() override {
        ok += 10;
        return *this;
    }
};

struct MU : X {
    int mu{};

    explicit MU(int mu) : X(mu), mu(mu) {}

    X &operator++() override {
        mu *= 5;
        return *this;
    }
};

int main() {
    X *x_base = new OK(0);
    ++x_base;
    std::cout << x_base->x;
    return 1;
};

Все, что я пытаюсь сделать, это использовать идею полиморфизма для операторов, в частности для оператора ++. Я хочу получить что-то вроде этого:


База* база = новая производная();

++base ‹--- ++ следует вызывать из класса Derivate


База* база2 = ned Derivate_2();

++base ‹--- ++ следует вызывать из класса Derivate_2


ОБНОВЛЕНИЕ:

Текущее решение моей проблемы — использовать ++(*base), о котором я уже знаю.

Но есть ли другой способ сделать ++base вместо ++(*base)?


Спасибо за помощь :)


person Vali    schedule 18.02.2019    source источник
comment
x_base является указателем на X. Увеличение указателя — это не то же самое, что применение оператора увеличения к объекту. Ваш код, печатающий x_base->x, имеет неопределенное поведение, поскольку после увеличения x_base не указывает на существующий объект. Измените приращение на ++(*x_base).   -  person Peter    schedule 18.02.2019
comment
На ваш обновленный вопрос ответ - нет. Вы не можете переопределить встроенные операторы необработанного указателя на Base.   -  person Öö Tiib    schedule 18.02.2019
comment
Это правда, но вы можете обернуть указатель в другой класс и написать перегрузку для этого класса-оболочки.   -  person john    schedule 18.02.2019


Ответы (3)


В этих двух строках

X *x_base = new OK(0);
++x_base;

вы создаете указатель на новый экземпляр, а затем увеличиваете указатель, а не указатель. Оператор приращения вашей иерархии классов никогда не вызывается, вместо этого вызывается встроенный оператор приращения для указателей. Вы можете исправить это, сначала разыменовав указатель:

++*x_base; // or ++(*x_base), might be more readable

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

OK ok(0);
X& x_base = ok;

++x_base; // now, x_base is a reference, no need to dereference it

Обратите внимание, что реализация вызываемой перегрузки оператора не изменяет значение X::x. std::cout << x_base->x; после приращения предполагает, что вы ожидаете, что значение будет ненулевым.

person lubgr    schedule 18.02.2019
comment
и в этом случае есть ли какое-нибудь решение, чтобы получить тот же результат, но вместо того, чтобы писать ++(*x), писать просто ++x? или это вообще невозможно. - person Vali; 18.02.2019
comment
@Vali Я думаю, этот ответ показывает, что вы можете решить свою проблему, задав переменной x тип X&, а не X*. Тогда ++x будет действовать на объект, а не на указатель. Честно говоря, я думаю, что это лучше, чем другой ответ. - person David K; 18.02.2019

Чтобы решить ваш второй вопрос, вам нужно написать оболочку для вашего класса указателя. Что-то вроде

class XPtr
{
public:
    XPtr(X* p) : ptr(p) {}
    X* operator->() { return ptr; }
    X& operator*() { return *ptr; }
    XPtr& operator++() { ++*ptr; return *this; }
private:
    X* ptr;
};

int main() {
    XPtr x_base = new OK(0);
    ++x_base;
    std::cout << x_base->x;
    return 1;
};
person john    schedule 18.02.2019
comment
Это имеет очень неприятный эффект создания чего-то, что действует как указатель, за исключением случаев, когда вы применяете к нему ++. Мне кажется, это нарушает принцип наименьшей неожиданности. - person David K; 18.02.2019

Не делай этого.

Во-первых, использование ++ для обозначения += 10 или *= 5, вероятно, должно привести к увольнению.

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

После того, как вы заставите ++ работать с указателем так, как вы хотите, вам лучше никогда не перебирать какие-либо массивы struct MU или struct OK.

Полиморфизм во время выполнения — это крайняя мера. Не беспокойтесь об этом, если вам это действительно не нужно.

Лучшее решение — избавиться от виртуальной и иерархии наследования и переопределения. В противном случае вы должны просто назвать функции вместо использования операторов.

Это не очень хорошая идея.

person KevinZ    schedule 21.02.2019