Переопределение бинарных операторов в производных классах - PullRequest
1 голос
/ 28 мая 2020

В настоящее время я работаю над проблемой алгебры. У меня есть (почти абстрактный) базовый класс, от которого будут производиться несколько классов. Все эти классы будут содержать списки чисел, упорядоченных по-разному. В базовом классе я хочу определить несколько операторов, которые будут реализованы для каждого из производных классов. Идея состоит в том, что как только эта библиотека будет завершена, мне больше не нужно будет заботиться о внутренностях производного класса. Как только я инициализирую некоторый производный класс, я могу ссылаться на него с помощью ссылки (или указателя) базового типа и через него получить доступ ко всем производным операциям.

Я хотел бы настроить довольно сложные алгоритмы, основанные на операции, которые были определены в базовом классе. Поэтому я хочу иметь доступ к этим алгоритмам только через базовый класс. Таким образом, его можно легко обобщить на многие типы производных классов. Я понял, что это именно то, что представляет собой объектно-ориентированное программирование, поэтому я остановился на C ++.

Я настроил большую часть того, что хочу, аналогично этому примеру (работает с g ++) :

#include <iostream>
class base;
base & gettype(const base&);
class base{
    public:
    int x;
    int type;
    base() = default;
    base(int in){
        this->type=0;
        this->x = in;
    }
    virtual base & operator+= ( const base & other){
        this->x += other.x;
        return *this;
    }
    virtual base & operator+ ( const base & other){
        base & ret = gettype(*this);
        ret += other.x;
        return ret;
    }
    virtual void print(){
        std::cout << "base is: " << x << "\n";
    }
};
class der1:public base{
    public:
    int a;
    der1(){}
    der1(int in){
        this->x = in;
        this->a = 2*in;
        this->type=1;

    }
    base & operator+= ( const base & other){
        std::cout <<"used der add\n";
        const der1 & otherder = static_cast<const der1 &>(other);
        this->x += otherder.x;
        this->a += otherder.a;
        return *this;
    }
    void print(){
        std::cout << "der1 is: " << x << " " << a << "\n";
    }
};
base & gettype(const base & in){
    if(in.type==0){
        return * new base();
    }
    if(in.type==1){
        return * new der1();
    }
}

main(){
    base baseobj(2);
    baseobj.print();
    baseobj += baseobj;
    baseobj.print();
    (baseobj+baseobj).print(); //Answer is right, but there is a memory leak

    der1 derobj(3);
    derobj.print();
    derobj += derobj;
    base * test = new der1(4);
    test->print();
    (*test) += (*test);
    test->print();
    base & test2 = *test;
    test2 += test2;
    test2.print(); //All these print the right answers as well
    delete test; 
}

Но внутри утечка памяти. Всякий раз, когда я делаю что-то вроде x=x+y, память, выделенная в функции gettype, больше не освобождается.

Я читал, что довольно редко, когда функция operator+ возвращает ссылку. Однако я не могу выполнить эту работу удовлетворительно, когда operator+ возвращается по значению. Причина в том, что когда он возвращается по значению, он вернет объект, нарезанный как объект base. Когда я определяю производные operator+ функции с ковариантными типами (как в примере ниже), они не используются, потому что я использую только base ссылки на типы, а не der1.

#include <iostream>
class base{
    public:
    int x;
    base() = default;
    base(int in){
        this->x = in;
    }
    virtual base & operator+= ( const base & other){
        this->x += other.x;
        return *this;
    }
    virtual base operator+ ( const base & other){
        base ret(*this);
        ret += other.x;
        return ret;
    }
    virtual void print(){
        std::cout << "base is: " << x << "\n";
    }
};
class der1:public base{
    public:
    int a;
    der1(int in){
        this->x = in;
        this->a = 2*in;
    }
    der1 & operator+= ( const der1 & other){
        this->x += other.x;
        this->a += other.a;
        return *this;
    }
    der1 operator+ ( const der1 & other){
        der1 ret(*this);
        ret += other.x;
        return ret;
    }
    void print(){
        std::cout << "der1 is: " << x << " " << a << "\n";
    }
};

main(){
    base baseobj(2);
    baseobj.print();
    baseobj += baseobj;
    baseobj.print();
    (baseobj+baseobj).print();  //This all works nicely for the base class

    der1 derobj(3);
    derobj.print();
    base * test = new der1(4);
    test->print(); //derived print function
    base & test2 = *test;
    test2 += test2; //base add function, because argument base&
    test2.print(); //Indeed, wrong answer.

}

Так можно создать библиотеку, которую я могу использовать примерно так:

base & x = getderived(3) // This will return a (reference/pointer to) derived type
base & y = getderived(3)
x +=y;
x = x+3*y;
//And a whole lot of other operations

delete x
delete y // I don't mine some manual memory management

Надеюсь, ясно, чего я хочу достичь. Если вы думаете, что это невозможно, я тоже рад этому ответу, тогда я знаю, что мне нужно перестать искать дальше. (Если нет решения, я сохраню свой текущий подход и буду работать с += операторами like, и пропущу двоичные. Это не совсем плохо)

1 Ответ

1 голос
/ 28 мая 2020

В C ++ вы никогда не должны использовать явные new / delete, и вы должны следовать RAII.

Если бы я понял вашу проблему, я бы избавился от type и gettype и вместо этого используйте виртуальный clone:

class base
{
public:
    virtual std::unique_ptr<base> clone() const
    {
        return std::make_unique<base>(*this);
    }
};

class derived : public base
{
public:
    std::unique_ptr<base> clone() const override
    {
        return std::make_unique<derived>(*this);
    }
};
...