Почему мы используем копирование и обмен при использовании оператора присваивания?
У меня есть вопрос об операторе присваивания при использовании метода копирования и замены.
String & operator = (String s) // the pass-by-value parameter serves as a temporary
{
s.swap (*this); // Non-throwing swap
return *this;
}// Old resources released when destructor of s is called.
Предположим, у нас есть хороший конструктор копирования, который копирует все указатели и динамически размещенные переменные.
Тогда какая разница между кодом выше и кодом ниже?
String & operator = (String s) // the pass-by-value parameter serves as a temporary
{
return s;
}
Поскольку у нас есть хороший конструктор копирования, я думаю, что другой объект, s, создается внутри функции operator=. Итак, какой смысл использовать функцию обмена без броска?
3 ответа
Основное отличие состоит в том, что 2-й operator=
не меняет текущий объект (т.е. *this
) совсем.
String a, b;
b = a; // b is not changed at all
И обратите внимание на 2-й operator=
возвращается s
(который будет уничтожен при выходе из функции) по ссылке, так что это будет просто привязанная ссылка.
Чтобы быть более общим, мы используем идиому copy & swap, чтобы обеспечить надежную гарантию безопасности исключений, которая является чем-то вроде семантики фиксации или отката; Если операция завершается из-за исключения, состояние программы останется неизменным.
String & operator = (String s) // Copy construct s. If exception happens here,
// s won't be constructed,
// and the state of the current object (*this) won't be changed
{
s.swap (*this); // Use non-throwing swap to commit the change
return *this; // Non-throwing operation
}
Есть несколько основных отличий:
Оператор присваивания должен вернуть *this
(и почти всегда так). Это то, что делает возможным назначение цепочек.
String s1, s2, s3;
s1 = s2 = s3; // Now all strings ave the same value.
Вместо этого вы возвращаете ссылку на локальную переменную. Затем он становится висячей ссылкой, поскольку он не указывает на допустимое место в памяти.
Кроме того, оператор присваивания должен изменить назначенный объект, чего не происходит в вашем коде.
И, наконец, зачем вообще нужен неработающий своп? Давайте предположим, что ваш оператор присваивания создает исключение. Каково состояние назначенного объекта после неудачного назначения?
Некоторые реализации могут привести к тому, что объект будет в недопустимом состоянии, так как исключение было сгенерировано в середине его изменения.
Вот где приходит своп. Так как своп никогда не выбрасывает, мы можем быть уверены, что назначенный объект находится в допустимом состоянии. Если перед свопом выдается исключение, объект все еще имеет свое старое значение, а если он был после свопа, то у объекта новое значение.
Разница в том, что во втором блоке кода вы создаете копию s
из String
объект, вернуть ссылку на него, а затем копия уничтожается при выходе из области видимости, что приводит к неопределенному поведению вашей программы.