14.4. Операторы присвоения
Кроме операторов присвоения копии и присваивания при перемещении, которые присваивают один объект типа класса другому объекту того же класса (см. раздел 13.1.2 и раздел 13.6.2), в классе можно определить дополнительные операторы присвоения, позволяющие использовать в качестве правого операнда другие типы.
Например, библиотечный класс vector, кроме операторов присвоения копии и присваивания при перемещении, определяет третий оператор присвоения, получающий заключенный в фигурные скобки список элементов (см. раздел 9.2.5). Этот оператор можно использовать следующим образом:
vector<string> v;
v = {"a", "an", "the"};
Такой оператор можно также добавить в класс StrVec (см. раздел 13.5):
class StrVec {
public:
StrVec &operator=(std::initializer_list<std::string>);
// другие члены, как в разделе 13.5
}
Чтобы не отличаться от операторов присвоения для встроенных типов (и уже определенных операторов присвоения копии и присваивания при перемещении), новый оператор присвоения будет возвращать ссылку на левый операнд:
StrVec &StrVec::operator=(initializer_list<string> il) {
// alloc_n_copy() резервирует пространство и копирует элементы
// из заданного диапазона
auto data = alloc_n_copy(il.begin(), il.end());
free(); // удалить элементы в этом объекте и освободить пространство
elements = data.first; // обновить переменные-члены, чтобы указывать
// на новое пространство
first_free = cap = data.second;
return *this;
}
Подобно операторам присвоения копии и присваивания при перемещении, другие перегруженные операторы присвоения должны освобождать существующие элементы и создавать новые. В отличие от операторов копирования и присваивания при перемещении, этот оператор не должен проверять случай присвоения себя себе. Параметр имеет тип initializer_list<string> (см. раздел 6.2.6), а это означает, что объект il не может быть тем же объектом, на который указывает указатель this.
Составные операторы присвоения
Составные операторы присвоения не обязаны быть функциями-членами. Однако все операторы присвоения, включая составные, предпочтительно определять в классе. Для согласованности со встроенными составными операторами присвоения эти операторы должны возвращать ссылку на левый операнд. Например, ниже приведено определение составного оператора присвоения для класса Sales_data.
// бинарный оператор-член:
// левый операнд связан с неявным указателем this
// подразумевается, что оба объекта относятся к той же книге
Sales_data& Sales_data::operator+=(const Sales_data &rhs) {
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
Упражнения раздела 14.4
Упражнение 14.20. Определите оператор суммы и составной оператор присвоения для класса Sales_data.
Упражнение 14.21. Напишите операторы класса Sales_data так, чтобы + осуществлял сложение, а оператор += вызывал оператор +. Обсудите недостатки этого подхода по сравнению со способом, которым эти операторы были определены в разделах 14.3 и 14.4.
Упражнение 14.22. Определите версию оператора присвоения, способного присвоить строку, представляющую ISBN, объекту класса Sales_data.
Упражнение 14.23. Определите в версии класса StrVec оператор присвоения для типа initializer_list.
Упражнение 14.24. Примите решение, нуждается ли класс из упражнения 7.40 раздела 7.5.1 в операторах копирования и присваивания при перемещении. Если да, то определите эти операторы.
Упражнение 14.25. Реализуйте все остальные операторы присвоения, которые должен определить класс. Объясните, какие типы должны использоваться как операнды и почему.