Effective C++ 学习笔记(8)
2015-05-27 18:33
155 查看
Term11: Handle assignment to self in operator =
1. 在=操作符内自我赋值有风险
我们可能不太会写出类似a=a这样的表达式。但如果说对于同一个对象,其使用不同的指针、引用、容器等等指向它的时候,我们就很难保证一定不会自我赋值。自我赋值的实现里,如果仅仅是类似于a.x=b.x这样的赋值,可能除了浪费性能外并没有太大的影响,真正的危险在于new、delete之类的操作。例如:
class Bitmap;
class Widget
{
...
Bitmap* pb;
};
Widget& Widget::operator=(const Widget& rhs)
{
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
可以看出,如果rhs就是this指向的对象,那么会出现pb指向的对象被销毁,而赋值依然是这个被销毁对象的地址。后续对这个对象的访问势必导致内存访问错误。
下面介绍几种处理方法。
2. 证同测试
就是检测到自我赋值就直接退出而不做任何工作,如下:
Widget& Widget::operator=(const Widget& rhs)
{
if(this == &rhs) return *this;
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
这种方法回避了自我赋值,但依然存在安全问题,如果pb = new Bitmap(*rhs.pb);开辟新空间没有成功,this->pb依然是无效的变量,访问它会导致内存错误。
3. 语序顺序控制
2中的代码是先delete再new,所以new的结果无法影响delete。如果先new,new失败了抛出异常,没有抛异常再delete,就可以避免这种情况。如下:
Widget& Widget::operator=(const Widget& rhs)
{
Bitmap* pOrig = pb;
pb = new Bitmap(*rhs.pb);
delete pOrig;
return *this;
}
4. Copy and swap
这种方法要求实现形如void Widget::swap(Widget& rhs);的方法,将rhs与*this的成员互换。代码如下:
Widget& Widget::operator=(const Widget& rhs)
{
Widget temp(rhs);
swap(rhs);
return *this;
}
由于这里调用了拷贝构造函数,可以将其简化为:
Widget& Widget::operator=(const Widget rhs)
{
swap(rhs);
return *this;
}
这种方式将直接调用拷贝构造函数改为了隐式调用。上一段代码由于是传引用,因此需要显式调用;而这里由于是传值,参数压栈时会自动调用,代码显得更加精巧。当然,这种精巧使得代码更晦涩,不见得是好事。
1. 在=操作符内自我赋值有风险
我们可能不太会写出类似a=a这样的表达式。但如果说对于同一个对象,其使用不同的指针、引用、容器等等指向它的时候,我们就很难保证一定不会自我赋值。自我赋值的实现里,如果仅仅是类似于a.x=b.x这样的赋值,可能除了浪费性能外并没有太大的影响,真正的危险在于new、delete之类的操作。例如:
class Bitmap;
class Widget
{
...
Bitmap* pb;
};
Widget& Widget::operator=(const Widget& rhs)
{
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
可以看出,如果rhs就是this指向的对象,那么会出现pb指向的对象被销毁,而赋值依然是这个被销毁对象的地址。后续对这个对象的访问势必导致内存访问错误。
下面介绍几种处理方法。
2. 证同测试
就是检测到自我赋值就直接退出而不做任何工作,如下:
Widget& Widget::operator=(const Widget& rhs)
{
if(this == &rhs) return *this;
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
这种方法回避了自我赋值,但依然存在安全问题,如果pb = new Bitmap(*rhs.pb);开辟新空间没有成功,this->pb依然是无效的变量,访问它会导致内存错误。
3. 语序顺序控制
2中的代码是先delete再new,所以new的结果无法影响delete。如果先new,new失败了抛出异常,没有抛异常再delete,就可以避免这种情况。如下:
Widget& Widget::operator=(const Widget& rhs)
{
Bitmap* pOrig = pb;
pb = new Bitmap(*rhs.pb);
delete pOrig;
return *this;
}
4. Copy and swap
这种方法要求实现形如void Widget::swap(Widget& rhs);的方法,将rhs与*this的成员互换。代码如下:
Widget& Widget::operator=(const Widget& rhs)
{
Widget temp(rhs);
swap(rhs);
return *this;
}
由于这里调用了拷贝构造函数,可以将其简化为:
Widget& Widget::operator=(const Widget rhs)
{
swap(rhs);
return *this;
}
这种方式将直接调用拷贝构造函数改为了隐式调用。上一段代码由于是传引用,因此需要显式调用;而这里由于是传值,参数压栈时会自动调用,代码显得更加精巧。当然,这种精巧使得代码更晦涩,不见得是好事。
相关文章推荐
- Effective C++ 学习笔记[1]
- Effective C++ 学习笔记(9)
- Effective C++ 学习笔记(13)
- Effective C++ 学习笔记(七)
- Effective C++学习笔记:初始化列表中成员列出的顺序和它们在类中声明的顺序相同
- Effective c++ 学习笔记(三)
- effective c++ 学习笔记之 Shifting from c to c++
- effectIve c++ 学习笔记
- “Effective C++ Third Edition”学习笔记(二)
- Effective C++ 学习笔记(6)
- Effective C++ 学习笔记(6)
- effective C++ 学习笔记 实现&&继承与面向对象设计
- Effective C++学习笔记 条款06:如不想使用编译器自动生成的函数,就该明确拒绝
- EffectiveC++学习笔记-条款5|6
- Effective C++ 学习笔记
- [More Effective C++ 学习笔记]异常
- Effective c++ 学习笔记之条款一视C++为一个语言联邦
- Effective C++ 学习笔记(10)
- Effective C++ 学习笔记(17)
- Effective C++ 学习笔记(21)