为什么我们在使用赋值运算符时要使用复制和交换?
Why do we use copy and swap when using assignment operator?
我对使用复制和交换方法时的赋值运算符有疑问。
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= 函数中创建的。
那么,使用非抛出交换函数有什么意义呢?
不同之处在于,在您的第二个代码块中,您创建了 String
对象的副本 s
,return 对其的引用,然后该副本在范围内被破坏退出,这会导致您的程序出现未定义的行为。
主要区别在于第二个 operator=
根本不会更改当前对象(即 *this
)。
String a, b;
b = a; // b is not changed at all
并注意第二个 operator=
通过引用返回 s
(当退出函数时将被销毁),所以它只是一个悬空引用。
更一般地说,我们使用copy & swap idiom来提供强大的异常安全保证,这有点像提交或回滚语义;如果一个操作因为异常而终止,程序状态将保持不变。
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
}
有几个主要区别:
赋值运算符应该 return *this
(而且几乎总是如此)。这就是使链接分配成为可能的原因。
String s1, s2, s3;
s1 = s2 = s3; // Now all strings ave the same value.
相反,您 return 对局部变量的引用。然后它变成一个悬空引用,因为它没有指向有效的内存位置。
此外,赋值运算符必须更改分配的对象,这在您的代码中不会发生。
最后,为什么我们需要非抛出交换?假设您的赋值运算符抛出异常。赋值失败后赋值对象的状态是什么?
一些实现可能会导致对象处于无效状态,因为异常是在更改它的过程中抛出的。
这就是交换的用武之地。因为交换从不抛出,我们可以确定分配的对象处于有效状态。如果在交换之前抛出异常,对象仍然有它的旧值,如果在交换之后,对象有它的新值。
我对使用复制和交换方法时的赋值运算符有疑问。
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= 函数中创建的。 那么,使用非抛出交换函数有什么意义呢?
不同之处在于,在您的第二个代码块中,您创建了 String
对象的副本 s
,return 对其的引用,然后该副本在范围内被破坏退出,这会导致您的程序出现未定义的行为。
主要区别在于第二个 operator=
根本不会更改当前对象(即 *this
)。
String a, b;
b = a; // b is not changed at all
并注意第二个 operator=
通过引用返回 s
(当退出函数时将被销毁),所以它只是一个悬空引用。
更一般地说,我们使用copy & swap idiom来提供强大的异常安全保证,这有点像提交或回滚语义;如果一个操作因为异常而终止,程序状态将保持不变。
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
}
有几个主要区别:
赋值运算符应该 return *this
(而且几乎总是如此)。这就是使链接分配成为可能的原因。
String s1, s2, s3;
s1 = s2 = s3; // Now all strings ave the same value.
相反,您 return 对局部变量的引用。然后它变成一个悬空引用,因为它没有指向有效的内存位置。
此外,赋值运算符必须更改分配的对象,这在您的代码中不会发生。
最后,为什么我们需要非抛出交换?假设您的赋值运算符抛出异常。赋值失败后赋值对象的状态是什么?
一些实现可能会导致对象处于无效状态,因为异常是在更改它的过程中抛出的。
这就是交换的用武之地。因为交换从不抛出,我们可以确定分配的对象处于有效状态。如果在交换之前抛出异常,对象仍然有它的旧值,如果在交换之后,对象有它的新值。