Производный класс наследует оператор присваивания базового класса?

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

В следующем коде я переопределяю оператор базового класса = в Derived, так что оператор присваивания производного класса по умолчанию вызывает перегруженный оператор =

#include <iostream>  
using namespace std;      
class Base  
{  
    public:  
    Base(int lx = 0):x(lx)  
    {  
    }  

    virtual Base& operator=( const Base &rhs)  
    {  
        cout << "calling Assignment operator in Base" << endl;  
        return *this;  
    }

    private:  
    int x;     
};      


class Derived : public Base  
{  
    public:  
    Derived(int lx, int ly): Base(lx),y(ly)  
    {  
    }

    Base& operator=(const Base &rhs)  
    {  
        cout << "Assignment operator in Derived"<< endl;  
        return *this;  
    }  

    private:  
    int y;    
};  



int main()  
{  
    Derived d1(10,20);  
    Derived d2(30,40);  
    d1 = d2;  
}  

Это дает результат

вызов оператора присваивания в Base

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


person Amit    schedule 17.01.2012    source источник


Ответы (5)


Компилятор генерирует оператор присваивания по умолчанию для Derived (который скрывает оператор Base). Однако оператор присваивания по умолчанию вызывает все операторы присваивания членов класса и базовых классов.

person Bo Persson    schedule 17.01.2012
comment
Могу ли я переопределить оператор базового класса = в производном классе? - person Amit; 17.01.2012
comment
@Amit: вы не можете переопределить это. По определению невиртуальную функцию нельзя переопределить, хотя ее можно скрыть. Возможно, вы хотите перегрузить оператор присваивания копии в Derived. Если да, то правильная подпись Derived& operator=(const Derived &rhs). Это сигнатура, которую ищет компилятор, когда вы делаете d1 = d2. Поскольку он не находит его, вместо этого он синтезирует оператор присваивания копирования по умолчанию, который вызывает Base::operator=(const Base &). - person Steve Jessop; 17.01.2012
comment
@Steve: я изменил код, и теперь мой оператор базового класса = виртуальный. - person Amit; 17.01.2012
comment
@Amit: это почти наверняка ошибка, операторы виртуального присваивания работают плохо. Вы не хотите, чтобы любой старый объект Base был назначен объекту Derived, потому что Base не предоставляет значения для присвоения y. - person Steve Jessop; 17.01.2012

Да, просто оператор = базового класса скрыт оператором = производного класса.

person Alok Save    schedule 17.01.2012
comment
Мне интересно: это объяснение кажется мне неверным, утверждает, что мы получим поведение, ожидаемое Амитом, и не объясняет наблюдаемое поведение. Если я не ошибаюсь, конечно. Почему плюсы? - person Christopher Creutzig; 17.01.2012

Если я неправильно понимаю, чего вы хотите достичь, вам нужен оператор присваивания для класса Derived, то есть тот, который принимает Derived в качестве входных данных:

class Derived : public Base  
{  
/* ... */
public:
    Derived& operator=(const Derived &rhs)  
    {  
        cout << "Assignment operator in Derived"<< endl;  
        return *this;  
    }
};  

Что произошло в вашем коде (уже объяснено в ответе Бо Перссона и комментариях к нему): в Derived вы реализовали оператор присваивания, который принимает экземпляр Base; но в main() вы назначаете экземпляр Derived; компилятор не увидел оператора присваивания для Derived (тот, который принимает Base, не считается), поэтому он сгенерировал оператор, который вызывает Base::operator=(), а затем присваивает членам данных Derived. Если бы вы определили назначение, как показано выше, этого не произошло бы, и был бы вызван ваш оператор; обратите внимание, что в этом случае присвоение Base и членов данных не будет происходить автоматически.


Другая ситуация, если вы действительно хотите получить назначение от Base до Derived, например. использовать его с другими производными от Base. Тогда определенный вами оператор будет работать, но чтобы применить его к экземпляру Derived, вам нужно привести этот экземпляр к Base:

Derived d1(10,20);  
Derived d2(30,40);  
d1 = static_cast<Base&>(d2);

Излишне говорить, что определенный вами оператор не может легко получить доступ к элементам данных rhs, специфичным для Derived: например. чтобы использовать rhs.y, вам нужно "преобразовать" rhs в Derived:

Derived& Derived::operator=(const Base& rhs)
{
    /* ... */
    Derived* rhs_d = dynamic_cast<Derived*>(&rhs);
    if( rhs_d )
        this->y = rhs_d->y;
}
person Alexey Kukanov    schedule 17.01.2012

Цитата из стандарта (12.8.24):

Поскольку оператор присваивания копирования/перемещения неявно объявлен для класса, если он не объявлен пользователем, оператор присваивания копирования/перемещения базового класса всегда скрыт соответствующим оператором присваивания производного класса (13.5.3). Объявление использования (7.3.3), которое вводит из базового класса оператор присваивания с типом параметра, который может быть типом оператора присваивания копирования/перемещения для производного класса, не считается явным объявлением такого оператора и не не подавлять неявное объявление оператора производного класса; оператор, введенный объявлением использования, скрыт неявно объявленным оператором в производном классе.

person ThomasMcLeod    schedule 15.10.2014

Если вы хотите добиться полиморфного поведения, это означает, что ваш базовый метод переопределен, вы должны были написать это:

int main (){
    Derived * d1 = new Derived (10,20);  
    Derived * d2 = new Derived (30,40);
    Base * b1 = d1 ;
    Base * b2 = d2 ; 
    *b1 = *b2 ;
    *d1 = *d2 ;
} 

Это приведет к следующему результату:

 $> Assignment operator in Derived.
 $> calling Assignment operator in Base. 

Итак, что я здесь делаю, это неявное использование динамического литья С++. Я не совсем уверен, работает ли это таким образом, но для того, чтобы высказать свою точку зрения, я думаю, что это будет полезно. Когда я вызываю оператор присваивания с разыменованными b1 и b2, программа сначала ищет переопределенный метод функции, вызываемой в производном классе, если он не находит его, то вызывает базовый класс методов. Теперь, когда я вызываю оператор присваивания из d1, программа приводит производный объект к базовому, и программа никак не может знать, что *d1 обозначает объект Derived, поэтому она вызывает базовый метод.

Это все люди.

person Gonzo RI    schedule 10.06.2013
comment
В коде отсутствуют определения классов, необходимые для его понимания, выходные строки получаются в неправильном направлении (предположительно, то есть как путем рассуждения, так и завершения кода) и упускается ключевой момент: то, что вы показываете здесь, является точным противоположность полиморфности и неявному использованию динамического литья С++ вообще. Если бы это было полиморфно, то присваивание *b1 = *b2 также сообщило бы, что оно вызывает производное operator=, но это не так (после того, как мы исправим ваши сообщения, которые были неправильными). (Попытка сделать Base.operator= методом virtual открывает другой мир боли) - person underscore_d; 15.09.2018