C ++ Возвращаемое значение, ссылка, константная ссылка

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

Значение:

Vector2D operator += (const Vector2D& vector)
{
this->x += vector.x;
this->y += vector.y;
return *this;
}

Неконстантная ссылка:

Vector2D& operator += (const Vector2D& vector)
{
this->x += vector.x;
this->y += vector.y;
return *this;
}

Const ссылка:

const Vector2D& operator += (const Vector2D& vector)
{
this->x += vector.x;
this->y += vector.y;
return *this;
}

В чем выгода этого? Я понимаю смысл передачи константной ссылки в функцию, поскольку вы хотите не изменять это значение, на которое ссылается ссылка внутри функции. Но меня смущает смысл возврата константной ссылки. Почему возврат ссылки лучше, чем возврат значения, и почему возврат константной ссылки лучше, чем возврат неконстантной ссылки?

30

Решение

Там нет разницы, если вы не напишите что-то странное, как

(v1 += v2) = v3;

В первом случае назначение будет временным, а общий эффект будет v1 += v2,

Во втором случае назначение будет v1, так что общий эффект будет v1 = v3,

В третьем случае назначение не будет разрешено. Это, вероятно, лучший вариант, поскольку такая странность почти наверняка является ошибкой.

Почему возврат ссылки лучше, чем возврат значения?

Это потенциально более эффективно: вам не нужно делать копию объекта.

и почему возврат константной ссылки лучше, чем возврат неконстантной ссылки?

Вы предотвращаете странности, как в приведенном выше примере, но при этом позволяете менее странные цепочки, такие как

v1 = (v2 += v3);

Но, как отмечено в комментариях, это означает, что ваш тип не поддерживает те же формы использования (ab), что и встроенные типы, что некоторые люди считают желательным.

19

Другие решения

Значение:

Возврат по значению означает, что вы возвращаете копию объекта. Это предъявляет требования к классу (он должен быть копируемым или перемещаемым). Это означает, что для объекта некоторых классов возврат по значению может быть дорогостоящим (в случае, когда RVO или NRVO не работает или выключен). Это также означает, что новый объект является независимым (в зависимости от его дизайна) от других объектов и является собственной ценностью. Это то, что вы, вероятно, должны возвращать из многих бинарных операторов, таких как +, -, * и так далее.

Неконстантная ссылка:

Вы действительно возвращаете псевдоним для другого объекта. Псевдоним, не являющийся const, позволяет вам изменять объект с псевдонимом. Это то, что вы должны возвращать из некоторых унарных операторов, таких как префикс ++ и — и * (разыменование), поскольку вы обычно хотите иметь возможность изменять возвращаемый объект.

Это возвращается оператором >> и оператором<< перегружен для потоков. Это позволяет создавать цепочки операторов:

cout << 5 << "is greater then" << 1 << endl;
cin >> myInt >> myFloat;

Вы также можете вернуть ссылку на * this, если хотите разрешить цепочку обычных методов, таких как эта:

object.run().printLastRunStatistics();

Const ссылка:

Как и выше, но вы НЕ МОЖЕТЕ изменить псевдоним объекта. Может использоваться вместо возврата по значению, когда подлежащий возврату объект является дорогостоящим для копирования и когда вы можете обеспечить его существование после возврата из функции.

Вот что обычно возвращает operator =, чтобы разрешить множественные присваивания таким образом, как их поддерживают стандартные типы:

a = b = c;

Const-ссылка, используемая в operator =, предотвращает использование такого типа (насколько я помню, не поддерживается стандартным типом):

++(a = b);

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

8

Это точно так же, как передача аргумента функции.

Вы хотите вернуть const ссылка, когда вы возвращаете свойство объекта, которое вы не хотите изменять вне его. Например: когда у вашего объекта есть имя, вы можете сделать следующий метод const std::string& get_name(){ return name; };, Какой самый оптимальный способ. Вы разрешаете доступ «только для чтения» к внутреннему свойству без копирования при возврате.

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

Например, вариант 3 не будет работать с чем-то вроде (v1 += v2).non_const_method(), В то время как следующее будет:

v1+=v2;
v1.non_const_method();
5

Разница между вернуться по значению а также вернуться по ссылке вступает в силу во время выполнения:

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

Когда вы возвращаете объект по ссылке, все вышеперечисленное не происходит, что приводит к повышению производительности.


Разница между вернуться по ссылке а также вернуться-на-постоянной ссылки не имеет никакого эффекта времени выполнения и просто защищает вас от написания ошибочного кода.

Например, с Vector2D& operator += (const Vector2D& vector), ты можешь сделать:

(x+=y)++ или же (x+=y).func() где func неконстантная функция в классе Vector2D,

Но с const Vector2D& operator += (const Vector2D& vector)компилятор выдаст ошибку для любой подобной попытки.

4

Как указывалось, но luk32 просто гарантирует, что никакие изменения не допускаются к объектам, возвращаемым этой функцией.
В основном это может помочь вам найти логические ошибки во время компиляции.
Предположим, вы уверены, что не измените объект, и ваш код изменяет объект, его можно отслеживать. Можно подумать о хорошей практике кодирования.

1