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.前 ...
随机推荐
- Codeforces Round #258 (Div. 2/B)/Codeforces451B_Sort the Array
解题报告 http://blog.csdn.net/juncoder/article/details/38102391 对于给定的数组,取对数组中的一段进行翻转,问翻转后是否是递增有序的. 思路: 仅 ...
- windows7旗舰版64位下安装、破解及执行QTP11报错
说明:假设你出现了下面几种情况,希望能解决你的问题:假设没有,就当路过. 1.安装qtp11时报vc++ 2005缺少,但怎么也不能安装成功 解决方法: 1.找到qtp安装包里面的vc++ 2005组 ...
- 【拆点费用流】【HDU1853】【 Cyclic Tour】
题意: 有N个城市,M条单向路,Tom想环游全部城市,每次至少环游2个城市,每个城市只能被环游一次.由于每条单向路都有长度,要求游遍全部城市的最小长度. // 给定一个有向图,必须用若干个环来覆盖整个 ...
- golang基础数据结构
一.概述: 这里主要讨论四种类型---数组.slice.map和结构体 数组和结构体是聚合类型:它们的值都是由很多个元素或者成员字段的值组成.数组是有同构元素组成--每个数组的元素的类型相同:结构体为 ...
- Erp第一章:初感
Erp第一章:初感1.核心标志实现:内部集成.外部集成.内部集成包括实现产品研发.核心业务.数据采集:外部集成就是企业与供需链上的所有合作伙伴的共享信息集成.2.Erp难点在于打破传统企业四面墙,把流 ...
- javascript设计模式——Publish/Subscribe
推荐阅读http://dev.housetrip.com/2014/09/15/decoupling-javascript-apps-using-pub-sub-pattern/ 我们先引出问题的所在 ...
- APP安全测评checklist
leader不要打我啊,我要借用一下我组app的安全测评检查方案,这些最基本的安全防范措施应该是每个app都要注意的吧: 对了,首先,你的app得先混淆啊~:AndroidStudio 混淆打包 先来 ...
- Oracle的汉字转拼音首字母的函数
CREATE OR REPLACE FUNCTION F_PINYIN(P_NAME IN VARCHAR2) RETURN VARCHAR2 AS V_COMPARE VARCHAR2(100); ...
- java中遍历map对象的多种方法
在Java中如何遍历Map对象 How to Iterate Over a Map in Java 在java中遍历Map有不少的方法.我们看一下最常用的方法及其优缺点. 既然java中的所有ma ...
- Linux系统重启network服务失败
问题描述 使用KVM通过修改配置文件配置好网卡IP,使用命令行service network restart 重启网络服务失败. 如图: 使用图形化管理工具配置IP,在系统界面右上角可以看到网卡状态为 ...