EC读书笔记系列之6:条款11 在operator=中处理自我赋值
记住:
★确保当对象自我赋值时operator=有良好行为。有三种方法:比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap技术
★确定任何函数若操作一个以上对象,而其中多个对象是同一个对象时,其行为仍然正确
------------------------------------------------------------------------------------
潜在的自我赋值举例:
a[i] = a[j]; //若i和j相同,便是自我赋值
*px = *py; //若px和py恰巧指向同一个东西
这些潜在的自我赋值是别名(所谓别名就是有一个以上的方法指称某对象)带来的结果。实际两个对象只要来自于同一个继承体系,他们甚至不需要声明为相同类型就可能造成“别名”,∵一个base class的reference或pointer可以指向一个derived class对象。
举例:下面的operator=代码,表面合理,但自我赋值时不安全
class Bitmap {...};
class Widget {
...
private:
Bitmap *pb; //含有指针或引用的类在写copying函数时就要注意!本能!
};
Widget& Widget::operator=( const Widget& rhs ) {
delete pb; //停止使用当前的bitmap
pb = new Bitmap( *rhs.pb );
return *this;
}
上面存在的问题:*this和rhs有可能是同一个对象。这样delete就不只是销毁当前对象的bitmap,它也销毁rhs的bitmap。
解决方案一:比较“来源对象”和“目标对象”的地址(证同测试),经典解法,初级!!!
Widget& Widget::operator=( const Widget& rhs ) {
if( this == &rhs )
return *this; //证同测试,是自我赋值的话就不做任何事
delete pb;
pb = new Bitmap( *rhs.pb );
return *this;
}
此版可行,但仍存在异常方面的麻烦:若new Bitmap导致异常(有可能因为分配时内存不足或因为Bitmap的copy constructor抛出异常),Widget最终会持有一个指针指向一块被删除的Bitmap。
解决方案二:手工排列语句顺序
Widget& Widget::operator=( const Widget& rhs ) {
Bitmap *pOrig = pb; //记住原先的pb
pb = new Bitmap( *rhs.pb );
delete pOrig;
return *this;
}
这段代码好处有二:
异常安全: 若new Bitmap失败,pb保持原状;
避免自我赋值:∵对原Bitmap做了一份复件、删除原Bitmap、再指向新制造的那个复件。
此种方法可能不是最高效的办法!!!但行得通。
解决方案三:使用copy-and-swap技术
class Widget {
...
void swap( Widget &rhs ); //交换*this和rhs的数据,详见条款29
...
};
Widget& Widget::operator=( const Widget& rhs ) {
Widget temp( rhs ); //为rhs数据制作一份复件---此即copy
swap( temp ); //将*this数据和上述复件的数据交换--此即swap
return *this;
}
方案三还有个改进版:
Widget& Widget::operator=( Widget rhs ) { //注意这里是传值,∴rhs是被传对象的副本
swap( rhs ); //将*this数据和上述复件的数据交换
return *this;
}
此版将copying动作从函数本体内移至函数参数构造阶段可令编译器有时生成更高效的代码
一个面试题目:写出String类的赋值运算符
class string{
public:
string& operator=( string rhs ){
swap( rhs );
return *this;
}
void swap( string& rhs ){
std::swap( _data, rhs._data );
}
private:
char* _data;
}
EC读书笔记系列之6:条款11 在operator=中处理自我赋值的更多相关文章
- Effective C++ -----条款11: 在operator=中处理“自我赋值”
确保当对象自我赋值时operator=有良好行为.其中技术包括比较“来源 对象”和“目标对象”的地址.精心周到的语句顺序.以及copy-and-swap. 确定任何函数如果操作一个以上的对象,而其中多 ...
- 读书笔记 effective c++ Item 11 在operator=中处理自我赋值
1.自我赋值是如何发生的 当一个对象委派给自己的时候,自我赋值就会发生: class Widget { ... }; Widget w; ... w = w; // assignment to sel ...
- Effective C++_笔记_条款11_在operator=中处理“自我赋值”
(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 为什么会出现自我赋值呢?不明显的自我赋值,是“别名”带来的结果: ...
- 11——在operator=中处理自我赋值
在operator=函数中加一个测试: if(&rhs==this) copy and swap
- EC读书笔记系列之5:条款9、条款10
条款9 绝不在构造和析构过程中调用virtual函数 记住: ★在构造和析构期间不要调用virtual函数,∵这类调用从不下降至derived class ---------------------- ...
- EC读书笔记系列之2:条款4 确定对象被使用前已先被初始化
条款4:确定对象被使用前已先被初始化 记住: ★为内置对象进行手工初始化,因为C++不保证初始他们 ★构造函数最好使用初始化列表,而不要在构造函数本体内使用赋值操作.初始化列表列出的成员变量,其排列次 ...
- Effective C++ 条款11:在operator=中处理"自我赋值"
"自我赋值"发生在对象被赋值给自己时: class Widget { ... }; Widget w; ... w = w; // 赋值给自己 a[i] = a[j]; // 潜在 ...
- Effective C++ 条款11,12 在operator= 中处理“自我赋值” || 复制对象时不要忘记每一个成分
1.潜在的自我赋值 a[i] = a[j]; *px = *py; 当两个对象来自同一个继承体系时,他们甚至不需要声明为相同类型就可能造成别名. 现在担心的问题是:假如指向同一个对象, ...
- EC读书笔记系列之11:条款20、21
条款20 宁以pass-by-reference-to-const替换pass-by-value 记住: ★尽量以pass-by-reference-to-const替换pass-by-value.前 ...
随机推荐
- C++ Primer笔记9_构造函数_拷贝构造(深拷贝与浅拷贝)
1.构造函数: >构造函数是一个特殊的.与类同名的成员函数,用于给每一个成员设置适当的初始值. >构造函数不能有返回值,函数名与类名同样. >缺省构造函数时,系统将自己主动调用该缺省 ...
- Android 查看通讯录Contacts是否发生变化
目的:确定通讯录是否发生变化 根据:參见ContactsContract.RawContacts类中的VERSION常量,该值是仅仅读的,当通讯录发生变化时,都会使该值变化 方法:version值是相 ...
- ORACLE 表连接详解
在ORACLE中,表连接方式主要有:内连接,外连接,自连接: 内连接: 这是最常用的连接查询 SELECT * FROM A INNER JOIN B ON A.ID=B.ID SELECT * FR ...
- CSS3 过渡
通过 CSS3,我们可以在不使用 Flash 动画或 JavaScript 的情况下,当元素从一种样式变换为另一种样式时为元素添加效果. 请把鼠标移动到右侧的元素上: 浏览器支持 Internet E ...
- 详解Linq to SQL
第一部分,什么是Linq to sql Linq to sql(或者叫DLINQ)是LINQ(.NET语言集成查询)的一部分,全称基于关系数据的 .NET 语言集成查询,用于以对象形式管理关系数据,并 ...
- Android开发环境的搭建之(二)Android Studio的安装
(1) 下载AS(android studio)1.3.2并安装android-studio-bundle-141.2178183-windows.exe.下载官方链接http://www.andr ...
- 【IOS学习基础】OC类的相关
几天前突然在别人的类的.m文件中看到这么一句代码:@synthesize xxxx = _xxxx; 当时愣是没理解啥意思,过后才缓过神来发现原来是把一些类的基础知识忘记了,虽然不用过多去深究以前的一 ...
- php的SQL连接操作的方法
初学php,于是一开始就在sql(具体点说是mysql)里受挫严重.于是这里记下一些方法. 首先是基本方法.基本方法现在我不用了,所以我就附一下hustoj中的部分吧 基本方法的数据库连接操作: if ...
- fragment嵌套,viewpager嵌套 不能正确显示
转帖:http://blog.csdn.net/mybook1122/article/details/24003343 通常为 viewPager.setAdapter(new MyFragmentP ...
- lex 和 yacc 的区别与联系
lex负责词法解析,而yacc负责语法解析,其实说白了就是lex负责根据指定的正则表达式,将输入的字符串匹配成一个一个的token,同时允许用户将当前匹配到的字符串进行处理,并且允许返回一个标识当前t ...