https://harttle.land/2015/07/30/effective-cpp-11.html,这个讲的很不错!
1.存在的问题
因为cpp中有指针和引用,它们可以指向同一个变量,所以会存在自赋值的问题。
a[i] = a[j]; //@ 如果i和j有同样的值,这里就是一次自赋值 *px = *py; //@ 如果px和py指向相同的东西,这里就是一次自赋值
自赋值一般存在:自赋值安全和异常安全,两个问题,例如:
自赋值安全:
Widget& Widget::operator=(const Widget& rhs){ delete pb; // stop using current bitmap pb = new Bitmap(*rhs.pb); // start using a copy of rhs's bitmap return *this; // see Item 10 }
当是自赋值的时候,pb已经先被删除了,那么后面的new就会为空,这是未知的计算。
异常安全:
Widget& Widget::operator=(const Widget& rhs){ if (this == &rhs) return *this; delete pb; // stop using current bitmap pb = new Bitmap(*rhs.pb); // start using a copy of rhs's bitmap return *this; // see Item 10 }
这个自赋值安全,但是没有异常安全,如果new处出现了异常,那么pb仍旧指向空。
2.解决办法
通过调整语句顺序:
Widget& Widget::operator=(const Widget& rhs){ Bitmap *pOrig = pb; // remember original pb pb = new Bitmap(*rhs.pb); // make pb point to a copy of *pb delete pOrig; // delete the original pb return *this; }
通过一个中间变量来实现,而没有使用if的判断,因为可能会影响效率。
其实也可以这样:
HasPtr & operator=(const HasPtr& hp){ std::string * t=new std::string(*hp.ps); delete ps; ps=t; i=hp.i; return *this; }
主要就是申请一个临时变量来指向原来的空间或者是新申请的空间。//delete应该是不会出现异常的吧。
通过swap,赋值和交换
Widget& Widget::operator=(Widget rhs){ swap(rhs); // swap *this's data with return *this; // the copy's }
注意参数为值拷贝,在cpp459页有讲解,
总结:
- 判断两个地址是否相同
- 仔细地排列语句顺序
- Copy and Swap
自赋值存在的知识点差不多这些。