不检查复制赋值运算符是不是将对象分配给自身真的安全吗?

Posted

技术标签:

【中文标题】不检查复制赋值运算符是不是将对象分配给自身真的安全吗?【英文标题】:Is it really safe not to check in the copy assignemt operator whether an object is assigned to itself?不检查复制赋值运算符是否将对象分配给自身真的安全吗? 【发布时间】:2018-12-10 22:01:14 【问题描述】:

这是我发现的实现“三法则”的示例:

class Array  
    public: 
        int size; 
        int* vals;
        Array() : size(0), vals(NULL)
        Array( int s, int* v );
        Array(const Array&); // 1
        Array& operator=(const Array&); // 2
        ~Array(); // 3
; 

Array::~Array()  
   delete []vals; 
   vals = NULL; 
 

Array::Array( int s, int* v )
    size = s; 
    vals = new int[size]; 
    std::copy( v, v + size, vals ); 
 

Array::Array(const Array& rhs):
    size(rhs.size),
        vals((rhs.size) ? new int[size] : NULL)

    if(size)
        std::copy(rhs.vals, rhs.vals + rhs.size, vals);


Array& Array::operator=(const Array& rhs)
//  if(this == &rhs) // no need
//      return *this;
    int* a = (rhs.size)? new int[rhs.size] : NULL; // this is why we don't need the above check: if this line throws then vals is untouched. if this is ok (local variable) then continue (next won't throw).
    std::copy(rhs.vals, rhs.vals + rhs.size, a); // copying to a even (self assignment won't harm)
    delete[] vals;
    vals = a;
    size = rhs.size;

    return *this;

正如您在上面看到的,复制赋值运算符中的检查被删除,因为创建局部变量然后删除成员指针并将其分配给局部变量。但是如果我写,我最重要的是:

int main() 
   int vals[ 4 ] =  11, 22, 33, 44 ;  
   Array a1( 4, vals ); 

   a1 = a1; // here I assigned a1 to itself

    return 0;

我将a1 分配给自己,这是否意味着a1vals 被删除,然后在复制分配运算符中分配了本地的a?这是正确的方法吗?我的代码是否有一些陷阱?

非常感谢任何提示和建议。

【问题讨论】:

a1 = a1;给你带来了什么问题?您的复制分配运算符看起来可以正确处理它,但效率不高。 @NathanOliver:如何提高效率? 大多数(可能存在“异常”)代码需要(为了正确性)检查不是异常安全的,可能需要一个 catch 子句在新抛出的情况下不要让对象处于错误状态。 @curiousguy:你的意思是我应该添加异常处理? 请注意,您所做的基本上是手动实现copy and swap idiom。如果你对自我分配的复制成本没意见,你可能想用它,恕我直言,它更干净。 【参考方案1】:

复制赋值运算符将起作用,从某种意义上说,自赋值将具有预期的行为:数组的值内容将保持不变。

不做测试你失去的是,如果你已经持有一个数组,你将分配一个你不需要的数组,执行一个你不需要做的副本,然后销毁你可以做的东西保留了。

这是否对性能很重要...这在很大程度上取决于依赖于使用情况的因素。毕竟,这只有在您执行自复制时才会出现问题。你多久这样做一次?如果你真的不经常(或永远)不这样做,那么测试只是你不需要的条件分支。

同时考虑vector的定义。当指向vector 的迭代器/指针/引用失效时,它具有非常特殊的情况。并且复制vector不是其中之一。因此,如果对象存储的位置是您的数组类型接口的一部分(vector 的方式),那么进行自复制对您的接口有更广泛的影响。

【讨论】:

谢谢你。最后一个问题:那么每当我达到“三法则”时,我是否需要使用“复制和交换”?

以上是关于不检查复制赋值运算符是不是将对象分配给自身真的安全吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何避免共享指针的复制赋值运算符c ++

cpp►动态内存分配与析构之复制构造函数/赋值运算符

C++ 隐式生成的赋值运算符的异常安全性

在 Javascript 中使用赋值运算符将一个对象设置为等于另一个对象

将从方法返回的引用分配给变量

当我们复制/分配派生类对象时,如何在继承中复制基类成员?