Почему operator = возвращает *это?
скажем, я хочу переопределить operator =
Так что я могу сделать что-то вроде
Poly p1; // an object representing a polynomial
Poly p2; // another object of the same type
p2 = p1; // assigns all the contents of p1 to p2
тогда в моей реализации operator =
, у меня что-то вроде этого:
Poly& Poly::operator=(const Poly &source) {
// Skipping implementation, it already works fine…
return *this;
}
не возражайте против реализации, она уже работает нормально.
меня беспокоит то, что что происходит, когда вы return *this
? Я знаю, что он возвращает ссылку на объект, но это то, что происходит?
p2 = &p1
5 ответов
вы return *this
таким образом, вы можете написать нормальное соединение C++ =
заявления типа:
Poly p1; //an object representing a polynomial
Poly p2;
Poly p2;
// ...
p3 = p2 = p1; //assigns all the contents of p1 to p2 and then to p3
потому что это утверждение в основном:
p3.operator=(p2.operator=(p1));
если p2.operator=(...)
не return *this
у вас не будет ничего значимого, чтобы перейти в p3.operator=(...)
.
p2 = p1
это сокращение от p2.operator=(p1)
. Это просто вызов вашего operator=
функция, которая возвращает ссылку на p2
, который вы потом игнорируете. Чтобы прояснить это, давайте назовем это assign
вместо operator=
:
Poly& Poly::assign(const Poly &source) {
.
.
.
return *this;
}
теперь вместо p2 = p1
, ты бы написал
p2.assign(p1);
в этом случае, результат вызова assign
игнорируется, но вы не должны игнорировать его. Например, вы можете написать:
p3.assign(p2.assign(p1));
используя operator=
вместо assign
, это становится
p3 = (p2 = p1);
но поскольку присваивание является правоассоциативным, это также может быть записано как
p3 = p2 = p1;
эта форма возможности выполнять сразу несколько заданий первоначально происходит от C и была сохранена в C++ через соглашение о возврате *this
на operator=()
.
может возникнуть соблазн сделать оператор копирования-назначения return void
Если вам никогда не понадобятся цепные задания (как показано в других ответах) в любом случае. В конце концов, цепные задания часто трудно читать и понимать, поэтому не разрешать их можно считать улучшение.
однако часто упускается из виду, что void operator=(Poly& const)
означает, что ваш тип больше не будет fulfuill в CopyAssignable
концепция, которым требуется T&
тип возвращаемого значения.
тип, который не выполнит CopyAssignable
концепция не может официально использоваться для некоторых операций стандартного контейнера, например std::vector::insert
, что означает, что следующий, казалось бы, невинный фрагмент кода дает неопределенное поведение, хотя он, вероятно, работает отлично:
#include <vector>
struct Poly
{
void operator=(Poly const&) {} // Poly is not CopyAssignable
};
int main()
{
std::vector<Poly> v;
Poly p;
v.insert(v.begin(), p); // undefined behaviour
}
как объясняет стандарт C++ в § 17.6.4.8 / 2.3, где он говорит об ограничениях на программы, использующие стандартную библиотеку:
(...) эффекты неопределено в следующих случаях:
(...) для типов, используемых в качестве аргументов шаблона при создании экземпляра a компонент шаблона если операции с типом не реализуют семантика применимых требований раздел (...).
конечно, это точно , потому что неопределенного поведения, которое компилятор может игнорировать ошибку и сделать программу ведите себя хорошо, подстраиваясь под явно намеченное поведение. Но этого не требуется.
вы должны также рассмотреть, что вы не можете предсказать все будущие использования Poly
тип. Кто-то может написать некоторую функцию шаблона, такую как:
template <class T>
void f(T const& t)
{
T t2;
T t3 = t2 = t;
// ...
}
эта функция не будет работать с вашим Poly
класса.
просто не нарушайте это соглашение C++, и вы не столкнетесь с проблемами.
что происходит, когда вы вернетесь *этот?
в вашем примере (p2 = p1;
), ничего. Метод копирует p1
на p2
и возвращает ссылку на объект "this", который вызывающий код не использует.
в коде, таких как p3 = p2 = p1;
первая ссылка-это p2 = p1
, копии p1
на p2
и возвращает ссылку на p2
. Затем вызывающий код копирует из этой ссылки-на -p2
на p3
(и игнорирует ссылка на p3
что возвращается).
(попутно: ваши модульные тесты гарантируют, что p1 = p1
работает правильно? Легко забыть это дело!)
возврат ссылки на целевой объект позволяет назначить цепочку (каскадирование), а перегрузка операторов внутри класса следует за правой ассоциативной (нажмите здесь для подробных правил перегрузки оператора)
Poly a, b, c;
a = b = c;