Наследование оператора присваивания

Есть этот код:

#include <iostream>

class Base {
public:
    Base(){
        std::cout << "Constructor base" << std::endl;
    }
    ~Base(){
        std::cout << "Destructor base" << std::endl;
    }
    Base& operator=(const Base& a){
        std::cout << "Assignment base" << std::endl;
    }
};

class Derived : public Base{
public:

};

int main ( int argc, char **argv ) {
    Derived p;
    Derived p2;
    p2 = p;
    return 0;
}

Результат после компиляции с помощью g++ 4.6:

Constructor base
Constructor base
Assignment base
Destructor base
Destructor base

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

Ответы

Ответ 1

У вас нет значения по умолчанию

Derived& operator=(const Base& a);

в вашем классе Derived.

Создается оператор присваивания по умолчанию:

Derived& operator=(const Derived& a);

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

Ответ 2

Собственно, то, что называется, неявно определено operator = для Derived. Определение, предоставленное компилятором, в свою очередь вызывает operator = для Base, и вы видите соответствующий вывод. То же самое с конструктором и деструктором. Когда вы оставляете его компилятору для определения operator =, он определяет его следующим образом:

Derived& operator = (const Derived& rhs)
{
    Base1::operator =(rhs);
    ...
    Basen::operator =(rhs);
    member1 = rhs.member1;
    ...
    membern = rhs.membern; 
}

где Base1,...,Basen являются основами класса (в порядке их указания в списке наследования), а member1, ..., membern являются членами Derived (не считая членов, которые были унаследованы) в том порядке, в котором вы их объявили в определение класса.

Ответ 3

Вы также можете использовать "using":

class Derived : public Base{
public:
    using Base::operator=;
};

http://en.cppreference.com/w/cpp/language/using_declaration

Я прочитал этот пост несколько раз, прежде чем кто-то помог мне с этим.

Ответ 4

Стандарт говорит (12.8):

Оператор присваивания должен выполняться нестационарным членом функция с одним параметром. Поскольку назначение копии оператор operator = неявно объявлен для класса, если не объявлен пользователем (12.8), оператор присваивания базового класса всегда скрыт оператором присваивания копии производного класса.

а затем оператор присваивания производного вызова вашей базы

Неявно определенный оператор присваивания копирования/перемещения для неединичного класс X выполняет поэтапное копирование/перемещение назначения своих подобъектов. Прямые базовые классы X назначаются первыми, в порядке их объявление в списке-спецификаторе базы, а затем немедленное нестатические элементы данных X назначаются в том порядке, в котором они были объявлены в определении класса.

Ответ 5

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

Ответ 6

Оператор присваивания действительно не унаследован. Наследование этого оператора позволит вам назначить Base для Derived, однако Base b; p = a; (по праву) не скомпилируется.

Что происходит, так это то, что компилятор генерирует operator=, так как вы не определили пользовательский для Derived. Автогенерированный operator= вызовет операторы присваивания всех базовых классов и всех членов. В этом аспекте он почти такой же, как конструкторы/деструкторы, которые также называют соответствующую функцию для всех базовых элементов/членов.