Effective c++学习笔记(item10 item11)
2021-10-11 本文已影响0人
懒生活
Effective c++学习笔记(item10 item11)
item10 是最简单的一节,item11和item10完全可以归并成一章。
实际上Scott通过item10 和item11 告诉了我们拷贝赋值函数的标准实现。包括拷贝复制函数的返回约定,和是否是自我赋值的保护限定。同时在item11中描述了另一种copy and swap的技术用于实现拷贝复制。
1 为什么拷贝赋值函数在重载时限定要返回自身类的引用类型?
这是因为操作符=的常用场景决定的,我们经常有类似的赋值语句a=b=c=11
,这个语句在执行时先执行c=11
然后把(c=11)的返回值(操作符=的返回值)继续赋值给b,以此类推。如果操作符=返回是空,那么就满足不了这种连续赋值的使用场景。所以赋值操作符不光要实现对象的内部赋值,还要返回一个可以用于连续赋值的数据。这个数据约定俗成是*this。只是约定,你硬要返回int,编译也能通过,但是那样是不推荐的。对于操作符=, +=, -=, *=, /=
都有上述的约定,他们的操作符重载模板如下
class TTT
{
TTT& operator=(const TTT& rhs)
{
...
return *this;
}
};
2重载拷贝复制操作符的时候需要判断是否是自我赋值
所谓自我赋值就是a=a的语句。当拷贝赋值涉及资源和指针的时候,自我赋值极其容易出现资源的操作错误。
观察如下的代码有可能出现的问题
class Widget
{
private:
Bitmap* pb;
}
Widget& Widget::operator=(const Widget& rhs)
{
delete pb;
pb = rhs->pb;
return *this;
}
当这个操作符重载函数作用于自己的时候,第一句话是释放自己指针指向的位置,第二句话是把自己指针给自己(但指针已经被无意间释放掉了),这样子返回的*this里面带了一个野指针。
为了解决这个问题,通常要添加一个证同测试
class Widget
{
private:
Bitmap* pb;
}
Widget& Widget::operator=(const Widget& rhs)
{
if(this == &rhs) return *this;
delete pb;
pb = rhs->pb;
return *this;
}
3 指针拷贝操作的"异常安全性"
观察下面两个代码实现
Widget& Widget::operator=(const Widget& rhs)
{
delete pb;
pb = fun(rhs->pb);
return *this;
}
Widget& Widget::operator=(const Widget& rhs)
{
Bitmap* pbbak = pb;
pb = fun(rhs->pb);
delete pbbak;
return *this;
}
从原则上要坚持使用第二段代码的使用顺序,就是对指针先赋值,确保赋值成功后再delete。在第二段代码中如果执行到pb=fun(rhs->pb)
中发出了异常,那么pb没有被赋值,同时因为异常也不会删除。 不像第一段代码先被删除,后赋值。那么赋值的时候如果发生了异常导致pb成为野指针。
4 copy and swap技术
这个后面单独章节描述