【问题标题】:Why does my asignment operator not work on self assignments?为什么我的赋值运算符不适用于自赋值?
【发布时间】:2013-09-22 14:28:18
【问题描述】:

当我执行 a = b 之类的操作时效果很好,但如果我执行 a = a,对于向量中的所有元素,我得到 -1.255 +-67。这是我的复制构造函数和赋值运算符:

VecXd(const VecXd &source){
    dimension = source.dimension;
    vector = new T[dimension];
    for(int i=0; i < dimension; i++)
        vector[i] = source.vector[i];
}

VecXd operator=(const VecXd &source){
    dimension = source.dimension;
vector = new T[dimension];
for(int i=0; i < dimension; i++)
    vector[i] = source.vector[i];
return *this;
}

【问题讨论】:

  • 你指的是哪一行?
  • 使用复制交换习语。
  • 另请注意,您并没有删除阵列。

标签: c++ operator-overloading assignment-operator


【解决方案1】:

简短回答:您正在覆盖您的 vector 指针,从而丢失数据(并泄漏内存)。

实现赋值运算符最简单的方法是复制和交换习语:

void swap(VecXd & other) {
    using std::swap;
    swap(dimension,other.dimension);
    swap(vector,other.vector);
}

VecXd& operator=(const VecXd &source){
    VecXd cpy (source);
    swap(cpy);
    return *this;
}

这是一个不错的选择,因为:

  • 自动分配工作
  • 不需要额外的delete
  • 如果你以后在你的类中添加其他成员,你只需要调整复制构造函数

奖励如果交换运算符不抛出(应该如此),这保证了强异常安全性,即如果赋值运算符抛出(通过复制构造函数)对象的状态保持不变.

【讨论】:

    【解决方案2】:
    VecXd operator=(const VecXd &source){
        dimension = source.dimension;
    vector = new T[dimension];
    for(int i=0; i < dimension; i++)
        vector[i] = source.vector[i];
                  ^
                 here you assign uninitialized vector values
                 to the same vector
    return *this;
    }
    

    引用尚未构造或已销毁的对象的非静态成员是未定义的行为。此外返回参考不是价值:

    VecXd& operator=(const VecXd &source){
       // ...
       return *this;
    }
    

    这将启用链分配a=b=c 并检查自分配:

    VecXd& operator=(const VecXd &source){
       if(this!=&source) {
           // ... do what needed
       }
       return *this;
    }
    

    【讨论】:

      【解决方案3】:

      在定义自定义赋值运算符时,您始终必须处理“自赋值”情况。 小样本:

      Shape& Shape::operator=(const Shape& S) //Assignment operator overloading
      {
          if(this==&source)       //Checking for self assignment.
          {       
              return *this;
          }
          m_id=S.m_id;
          return *this;
      }
      

      【讨论】:

        【解决方案4】:

        这是因为一旦分配了新向量,就会丢失 source.vector 的先前值。这是一个自赋值,所以 source 和 *this 指的是同一个对象,因此 vector 和 source.vector 是一回事。

        您可以修复此问题以及内存泄漏,如下所示:

        VecXd operator=(const VecXd &source){
            dimension = source.dimension;
            T *temp = new T[dimension]; // Don't loose source.vector yet
            for(int i=0; i < dimension; i++)
                temp[i] = source.vector[i];
            delete [] vector; // Delete old vector
            vector = temp;
            return *this;
        }
        

        更好的是,您可以防止自我分配以防止这种讽刺:

        VecXd operator=(const VecXd &source){
            if(this == &source)
                return *this; // This is a self-assignment, so there's nothing to do
            delete [] vector; // Delete old vector
            dimension = source.dimension;
            vector = new T[dimension]; // Now we are sure that vector and source.vector differ
            for(int i=0; i < dimension; i++)
                vector[i] = source.vector[i];
            return *this;
        }
        

        【讨论】:

        • 啊,先检查自赋值;聪明的。我只用“return”试了一下,它说我需要返回一个 VecXd。然后我用“返回源”尝试了它,它有效!谢谢!
        【解决方案5】:

        如果两个向量相同,那么这行会抹去源向量的内容:

        vector = new T[dimension];
        

        所以复制循环发生的下一行,你正在将垃圾读入垃圾。对于真正安全的赋值运算符/复制构造函数,请使用copy-swap pattern

        【讨论】:

        • 不,你说的不对。按值返回会导致垃圾数据的副本,但不会导致它。
        猜你喜欢
        • 1970-01-01
        • 2011-08-02
        • 2019-10-06
        • 1970-01-01
        • 2022-01-05
        • 2015-12-25
        • 1970-01-01
        • 1970-01-01
        • 2011-11-14
        相关资源
        最近更新 更多