浅谈operator= 实现

  • 抛出问题:小心"自我赋值"
  class HasPtr 
  {
    public:
        ......
        HasPtr& operator=(const HasPtr& rhs)  
        {
            delete ps;
            ps = rhs.ps;
            return *this;
        }
    private:
        string* ps;
  };

这样写是有问题的,因为operator=函数的2侧可能是同一个对象。如果我们这样处理,那么最终ps指向的是一个内存已经被释放的地址,其结果显然是未定义。

int main()
{
    HasPtr ptr;
    ptr = ptr;  // error
}
  • 改进1:添加证同测试
  class HasPtr 
  {
    public:
        ......
        HasPtr& operator=(const HasPtr& rhs)  
        {
            if (&rhs == this)
                return *this;
            delete ps;
            ps = new string(*rhs.ps);    // 这段代码有可能抛出异常
            return *this;
        }
    private:
        string* ps;
  };

在之前,我们就可以先增加一个证同处理,达到自我赋值的检验目的。
这样做虽然可以避免自我赋值,但它并不是异常安全的。假设在ps = new string(*rhs.ps)中,抛出了异常。那么我们此时已经改变了原对象(前一句 delete ps),违反了异常安全原则。

  • 改进2:确保异常安全
  class HasPtr
 {
    public:
        ......
        HasPtr& operator=(const HasPtr& rhs)  
        {
             auto *temp = ps;      // 记住原先的ps
             ps = new string(*rhs.ps);
             delete temp;          // 释放原先的ps
             return *this;
        }
    private:
        string* ps;
  }; 

现在即使ps = new string(*rhs.ps);这一步即使抛出异常,自身也能保持原状。同时也能处理自我赋值。它或许不是处理自我赋值的最高效方法,但他行得通。

  • 改进3 copy and swap
    class HasPtr  
    {
        public:
            void swap(HasPtr& rhs)  
            {
                std::swap(this->ps, rhs.ps);
            }
      
            HasPtr& operator=(const HasPtr& rhs) 
            {
                HasPtr temp(this);      // 调用拷贝构造函数
                swap(rhs);
                return *this;
            }    // 离开作用域,自动析构
        private:
          string* ps;
    };
    

它也有一个变体

class HasPtr  
{
   public:
       void swap(HasPtr& rhs)  
       {
           std::swap(this->ps, rhs.ps);
       }
       
       // 值传递
       HasPtr& operator=(HasPtr rhs)   // 调用拷贝构造函数
       {
           swap(rhs);
           return *this;
       }
   private:
     string* ps;
};

改进3是利用拷贝构造函数和自动变量离开作用域自动析构。

  • 总结
    综上一个好的operator=应该做到能处理自我赋值同时又确保异常安全,上面改进2和3都能做到。

参考资料:
《Effective C++ 改善程序与设计的55个具体做法》3th [美] Scott Meyers

你可能感兴趣的:(浅谈operator= 实现)