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.前 ...
随机推荐
- android卸载反馈实现
博客原地址:http://blog.csdn.net/wang_shaner/article/details/41543787 实现原理 fork分叉函数 fork分叉(分裂)函数可以创建一个新进程, ...
- 搭建PhoneCat项目的开发与测试环境
AngularJS官方网站提供了一个用于学习的示例项目:PhoneCat.这是一个Web应用,用户可以浏览一些Android手机,了解它们的详细信息,并进行搜索和排序操作. 获取源代码 PhoneCa ...
- HTML——JAVASCRIPT——关灯效果
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- [Linked List]Remove Linked List Elements
Total Accepted: 43183 Total Submissions: 160460 Difficulty: Easy Remove all elements from a linked l ...
- 提高php代码质量 36计
1.不要使用相对路径 常常会看到: ? 1 require_once('../../lib/some_class.php'); 该方法有很多缺点: 它首先查找指定的php包含路径, 然后查找当前目录. ...
- requireJS配置选项
1. baseUrl 当设置的目录最前面带有/,则是从电脑的根目录开始算起,若是baseUrl='another/path' 则是从require.js的目录开始算起 而且当require(['a. ...
- 获取自动增长IDmysqli函数
<?php $mysqli=@new mysqli("localhost", "root", "123456", "xsph ...
- HttpServletRequest 报错 myeclipese支持tomcat
做WEB项目的时候,别人拷贝了一份源码给你导入myeclipse结果发现一堆报错 HttpServletRequest 一排红色波浪线..... 这个原因主要是没有tomcat下的一个jar包 se ...
- C语言笔记——简介与编译过程初探
序言 从今天起,详细说说C语言.这一年多,在大多数语言和技术之间转了一大圈,终于看清楚了事实,决心静下心来好好学学C语言.初学者会认为C语言是个入门用的东西,没有必要深入研究.但对计算机领域再稍加了解 ...
- python sys.exit()函数说明
sys.exit()函数是通过抛出异常的方式来终止进程的,也就是说如果它抛出来的异常被捕捉到了的话程序就不会退出了. #!/usr/bin/python #!coding:utf-8 import s ...