C++ “检查自我分配”有什么问题,它是什么意思?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12015156/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is wrong with "checking for self-assignment" and what does it mean?
提问by Jimm Chen
In Herb Sutter's book Exceptional C++ (1999), he has words in item 10's solution:
在 Herb Sutter 的书Exceptional C++ (1999) 中,他在第 10 项的解决方案中有话:
"Exception-unsafe" and "poor design" go hand in hand. If a piece of code isn't exception-safe, that's generally okay and can simply be fixed. But if a piece of code cannot be made exception-safe because of its underlying design, that almost always is a signal of its poor design.
Example 1: A function with two different responsibilities is difficult to make exception-safe.
Example 2: A copy assignment operator that is written in such a way that it must check for self-assignmentis probably not strongly exception-safe either
“异常不安全”和“糟糕的设计”齐头并进。如果一段代码不是异常安全的,那通常没问题,可以简单地修复。但是,如果一段代码因其底层设计而不能成为异常安全,那几乎总是一个糟糕设计的信号。
示例 1:具有两种不同职责的函数很难做到异常安全。
示例 2:以必须检查自赋值的方式编写的复制赋值运算符可能也不是强异常安全的
What does he mean by the term "check for self-assignment"?
他所说的“检查自我分配”是什么意思?
[INQUIRY]
[询问]
Dave and AndreyT shows us exactly what "check for self-assignment" means. That's good. But the question is not over. Why does "check for self-assignment" hurts "exception safety"(according to Hurb Sutter)? If the caller tries to do self-assignment, that "check" works as if no assignment ever occurs. Does it really hurt?
Dave 和 AndreyT 向我们展示了“检查自我分配”的确切含义。那挺好的。但问题还没有结束。为什么“检查自我分配”会损害“异常安全”(根据赫布萨特的说法)?如果调用者尝试进行自赋值,则该“检查”的工作方式就好像从未发生过赋值一样。真的疼吗?
[MEMO 1] In item 38 Object Identitylater in Herb's book, he explains about self-assignment.
[备忘录 1] 在Herb 书中后面的第38 项对象标识中,他解释了自我分配。
回答by AnT
The more important question in this case is what "written in such a way that it must check for self-assignment" means.
在这种情况下,更重要的问题是“以必须检查自赋值的方式编写”是什么意思。
It means that a well designed assignment operator should not needto check for self-assignment. Assigning an object to itself should work correctly (i.e. have the end-effect of "doing nothing") without performing as explicit check for self-assignment.
这意味着设计良好的赋值运算符不应该需要检查自赋值。将对象分配给自身应该可以正常工作(即具有“什么都不做”的最终效果),而无需对自分配执行显式检查。
For example, if I wanted to implemented a simplistic array class along the lines of
例如,如果我想实现一个简单的数组类
class array {
...
int *data;
size_t n;
};
and came up with the following implementation of the assignment operator
并提出了以下赋值运算符的实现
array &array::operator =(const array &rhs)
{
delete[] data;
n = rhs.n;
data = new int[n];
std::copy_n(rhs.data, n, data);
return *this;
}
that implementation would be considered "bad" since it obviously fails in case of self-assignment.
这种实现将被认为是“糟糕的”,因为它在自我分配的情况下显然会失败。
In order to "fix" it one can either add an explicit self-assignment check
为了“修复”它,可以添加一个明确的自赋值检查
array &array::operator =(const array &rhs)
{
if (&rhs != this)
{
delete[] data;
n = rhs.n;
data = new int[n];
std::copy_n(rhs.data, n, data);
}
return *this;
}
or follow a "check-less" approach
或遵循“无检查”方法
array &array::operator =(const array &rhs)
{
size_t new_n = rhs.n;
int *new_data = new int[new_n];
std::copy_n(rhs.data, new_n, new_data);
delete[] data;
n = new_n;
data = new_data;
return *this;
}
The latter approach is better in a sense that it works correctly in self-assignment situations without making an explicitcheck for it. (This implementation is still far for perfect from the exception safety point of view, it is here to illustrate the difference between "checked" and "check-less" approaches to handling self-assignment). The later check-less implementation can be written more elegantly through the well-known copy-and-swap idiom.
后一种方法在某种意义上更好,因为它可以在自分配情况下正常工作,而无需对其进行明确检查。(从异常安全的角度来看,这个实现还很不完美,这里是为了说明处理自赋值的“检查”和“无检查”方法之间的区别)。通过众所周知的复制和交换习语,可以更优雅地编写后来的无检查实现。
This does not mean that you should avoid explicit checks for self-assignment. Such check do make sense from the performancepoint of view: there's no point in carrying out a long sequence of operations just to end up "doing nothing" in the end. But in a well-designed assignment operator such checks should not be necessary from the correctnesspoint of view.
这并不意味着您应该避免对自分配进行显式检查。从性能的角度来看,这种检查确实有意义:执行一长串操作只是为了最终“什么都不做”是没有意义的。但在设计良好的赋值运算符中,从正确性的角度来看,这种检查应该是不必要的。
回答by camino
From c++ core guide
来自 c++ 核心指南
Foo& Foo::operator=(const Foo& a) // OK, but there is a cost
{
if (this == &a) return *this;
s = a.s;
i = a.i;
return *this;
}
This is obviously safe and apparently efficient. However , what if we do one self-assignment per million assignments? That's about a million redundant tests (but since the answer is essentially always the same, the computer's branch predictor will guess right essentially every time). Consider:
这显然是安全的,而且显然是有效的。但是,如果我们每百万次作业做一次自分配呢?这大约是一百万个冗余测试(但由于答案基本上总是相同的,计算机的分支预测器基本上每次都会猜对)。考虑:
Foo& Foo::operator=(const Foo& a) // simpler, and probably much better
{
s = a.s;
i = a.i;
return *this;
}
Note: The code above only apply to classes without pointer, for classes with pointer point to dynamic memory. Please refer to Ant's answer.
注意:上面的代码只适用于没有指针的类,对于有指针指向动态内存的类。请参考蚂蚁的回答。
回答by David
MyClass& MyClass::operator=(const MyClass& other) // copy assignment operator
{
if(this != &other) // <-- self assignment check
{
// copy some stuff
}
return *this;
}
Assigning an object to itself is a mistake but it shouldn't logically result in your class instance changing. If you manage to design a class where assigning to itself changes it, it's poorly designed.
将对象分配给自身是一个错误,但从逻辑上讲,它不应该导致您的类实例发生更改。如果您设法设计一个类,其中分配给自身会改变它,那么它的设计很差。
回答by Puppy
The general reason to check for self-assignment is because you destroy your own data before copying in the new one. This assignment operator structure is also not strongly exception safe.
检查自赋值的一般原因是因为您在复制新数据之前销毁了自己的数据。这种赋值运算符结构也不是强异常安全的。
As an addendum, it was determined that self-assignment does not benefit performance at all, because the comparison must run every time but self-assignment is extremely rare, and if it does occur, this is a logical error in your program (really). This means that over the course of the program, it's just a waste of cycles.
作为附录,确定自赋值根本不会提高性能,因为每次都必须运行比较,但自赋值极为罕见,如果确实发生,这就是程序中的逻辑错误(真的) . 这意味着在整个程序过程中,这只是在浪费周期。