“检查自我分配”有什么问题?这是什么意思? [英] What is wrong with "checking for self-assignment" and what does it mean?

查看:87
本文介绍了“检查自我分配”有什么问题?这是什么意思?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Herb Sutter的书 Exceptional C ++(1999)中,他在第10项的解决方案中有文字:


异常不安全和不良设计并驾齐驱。如果一段代码不是异常安全的,那通常就可以了,可以简单地解决。但是,如果由于某段代码的底层设计而导致该段代码无法保证异常安全,那么这几乎总是表明其设计欠佳。



示例1:具有两个不同职责的函数很难使异常安全。



示例2:以必须检查自我分配的方式编写的副本分配运算符可能不是强烈例外-是安全的


检查自我分配一词是什么意思?



[询问]



Dave和AndreyT确实向我们展示了检查自我分配的含义。那很好。但是问题还没有结束。为什么检查自我分配会损害例外安全性(根据Hurb Sutter)?如果呼叫者尝试进行自我分配,则该检查就像没有分配一样进行。



[MEMO 1]在Herb的后面的第38条 Object Identity 中,他解释了自我分配。

p>

解决方案

在这种情况下,更重要的问题是以必须检查自我分配的方式编写是什么意思。 / p>

这意味着精心设计的赋值运算符应该不需要检查自我赋值。向其自身分配对象应该可以正常工作(即具有不做任何事情的最终效果),而无需执行对自赋值的显式检查。



例如,如果我想按照

 类数组{
...
int *数据;
size_t n;
};

并提出了赋值运算符的以下实现方式

  array& array :: operator =(const array& rhs)
{
delete [] data;

n = rhs.n;
data = new int [n];
std :: copy_n(rhs.data,n,data);

返回* this;
}

该实现将被认为是不好的,因为在自我的情况下它显然会失败-分配。



为了修复它,可以添加明确的自我分配检查

  array& array :: operator =(const array& rhs)
{
if(& rhs!= this)
{
delete [ ]数据;

n = rhs.n;
data = new int [n];
std :: copy_n(rhs.data,n,data);
}

return * this;
}

或遵循免签方法

  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 []数据;

n = new_n;
data = new_data;

返回* this;
}

后一种方法更好,因为它可以自我分配情况而无需进行显式检查。 (从异常安全的角度来看,此实现还远远不能达到完美,这里是要说明自检和免检方法在处理自我分配方面的区别)。可以通过众所周知的复制和交换惯用法更优雅地编写后面的免检查实现。



这并不意味着您应该避免对self进行显式检查-分配。从性能的角度来看,这种检查确实是有意义的:进行长时间的操作只是为了最终无所作为是没有意义的。但是从正确性的角度来看,在设计良好的赋值运算符中,这种检查是不必要的。


In Herb Sutter's book Exceptional C++ (1999), he has words in item 10's solution:

"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-assignment is probably not strongly exception-safe either

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?

[MEMO 1] In item 38 Object Identity later in Herb's book, he explains about self-assignment.

解决方案

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 need to 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 explicit check 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 performance point 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 correctness point of view.

这篇关于“检查自我分配”有什么问题?这是什么意思?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆