Производные классы косвенно наследуют оператор присваивания базы? - PullRequest
3 голосов
/ 15 января 2012

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

#include <iostream>
using namespace std;

class Base
{
public:
    void operator=(const Base& rf)
    {
        cout << "base operator=" << endl;
        this->y = rf.y;
    }
    int y;
    Base() : y(100) { }
};

class Derived : public Base
{
public:
    int x;
    Derived() : x(100) { }
};

int main()
{
    Derived test;
    Derived test2;
    test2.x = 0;
    test2.y = 0;
    test.operator=(test2); // operator auto-generated for derived class but...
    cout << test.x << endl << test.y << endl;
    cin.ignore();
    return 0;
}

ВЫВОД ПРОГРАММЫ:

> base operator=
>  0
>  0

Теперь, где я запутался: Правило говорит, что производный класс никогда не наследует оператор назначения, вместо этого он создаетего собственный operator= однако в этом примере базовый operator= вызывается для производного класса.

Во-вторых, я смог явно вызвать оператор присвоения для производного класса, который, в свою очередь, явно не определен впроизводный класс.

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

Ответы [ 4 ]

12 голосов
/ 15 января 2012

Сгенерированные автоматически вызывают оператор присваивания базового класса.

// generated version looks basically like this
Derived& operator=(Derived const& other){
  Base::operator=(static_cast<Base const&>(other));
  x = other.x;
  return *this;
}

Актерский состав призван избежать случайного вызова шаблонного Base::operator=, подобного этому:

template<class Other>
Base& operator=(Other const& other); // accepts everything

Или странный как этот:

// forward-declare 'Derived' outside of 'Base'
Base& operator=(Derived const& other); // accepts derived class (for whatever reason)

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

Компилятор автоматически объявляет оператор присваивания, если вы этого не делаете, и ваш класс это позволяет (т. Е. Нет ссылочных членов и некоторых других непонятных правил) и дополнительно определяет , если вы на самом деле используйте это где-нибудь.

4 голосов
/ 15 января 2012

Сгенерированный компилятором оператор присваивания вызывает оператор присваивания каждого подобъекта.Это включает в себя базовые классы и нестатические переменные-члены.

Стандарт гласит (раздел 12.8 [class.copy]):

Если определение класса не объявляет явно оператор копирования,один объявлен неявно.Если определение класса объявляет конструктор перемещения или оператор присваивания перемещения, неявно объявленный оператор присваивания копии определяется как удаленный;в противном случае он определяется как дефолтный (8.4).Последний случай считается устаревшим, если в классе имеется объявленный пользователем конструктор копирования или объявленный пользователем деструктор.Неявно объявленный оператор присваивания копии для класса X будет иметь форму

X&  X::operator=(const  X&)

, если

  • каждый прямой базовый класс B из X имеет копиюоператор присваивания, параметр которого имеет тип const B&, const volatile B& или B и
  • для всех нестатических членов данных X, которые относятся к типу класса M (или массиву)каждый такой тип класса имеет оператор присваивания копии, параметр которого имеет тип const M&, const volatile M& или M.

В противном случае неявно объявленный оператор присваивания копии будет иметьform

X&  X::operator=(X&)

и

Неявно определенный оператор назначения копирования / перемещения для не объединенного класса X выполняет членское копирование / перемещение назначения своих подобъектов,Прямые базовые классы X назначаются вначале в порядке их объявления в base-specier-list , а затем назначаются непосредственные нестатические элементы данных X впорядок, в котором они были объявлены в определении класса.Пусть x будет либо параметром функции, либо, для оператора перемещения, значением x, относящимся к параметру.Каждый подобъект присваивается способом, соответствующим его типу:

  • , если подобъект имеет тип класса, как если бы путем вызова operator= с подобъектом в качестве выражения объекта и соответствующим подобъектомx в качестве единственного аргумента функции (как будто путем явной квалификации; то есть игнорируя любые возможные виртуальные переопределяющие функции в более производных классах);
  • , если подобъект является массивом, каждый элемент назначается вспособ, соответствующий типу элемента;
  • , если подобъект имеет скалярный тип, используется встроенный оператор присваивания.

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

2 голосов
/ 15 января 2012

Это потому, что неявно определенный оператор = вызывает оператор базовых классов =.Прочтите FAQ:

Я создаю производный класс;должен ли мой оператор присваивания вызывать оператор присваивания моего базового класса?

Да (если вам нужно определить оператор присваивания в первую очередь).

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

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

0 голосов
/ 15 января 2012

4 вещи никогда не наследуются Конструктор Copy-конструктор Оператор присваивания Деструктор

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

...