EC笔记:第二部分:11:在operator=中处理“自我赋值”
已经一年半没有写过博客了,最近发现学过的知识还是需要整理一下,为知笔记,要开始收费了以前写在为知笔记上笔记也会慢慢的转到博客里。
话不多说,进入正题。
考虑考虑以下场景:
当某个对象对自身赋值时,会出现什么现象??
例子:
#include <iostream>
class A {
private:
int
*arr;
public:
A()
{
arr =
new
int[256];
}
~A()
{
delete arr;
}
const A&
operator=(const A &other)
{
delete arr; //清除原来的值
arr =
new
int[256]; //重新分配内存
std::memcpy(arr, other.arr,
256
*
sizeof(int)); //赋值
return
*this;
}
};
在这段代码中,类A管理了256个整数的数组,当发生赋值操作时,对象先将自身管理的内存区释放,然后重新分配内存并且赋值(这里可以直接进行内存拷贝,为了演示,做了删除并重新分配操作,假设这里是个vector,想象一下^_^)。这个实现在应对大多数情况是没有问题的。如:
int main()
{
A a;
A b;
a = b;
}
这样完全没有问题。但是,假设出现以下场景:
int main()
{
A a;
A &b = a;
//若干操作
a = b;
}
a和b表示的是同一个对象,那么在重新分配内存之前,就会将arr(a和b是同一个)指向的内存区域释放。然后在做memcpy的时候程序就会崩溃(引用了已释放的内存区域)。
重新对class A的operator=实现:
#include <iostream>
class A {
private:
int
*arr;
public:
A()
{
arr =
new
int[256];
}
~A()
{
delete arr;
}
const A&
operator=(const A &other)
{
if(this
==
&other)
return
*this;
delete arr; //清除原来的值
arr =
new
int[256]; //重新分配内存
std::memcpy(arr, other.arr,
256
*
sizeof(int)); //赋值
return
*this;
}
};
改进后,判断当前如果赋值和被赋值的是同一个对象,就直接返回,可以避免释放掉同一块内存。
这段代码虽然可以避免赋值上的问题,但是存在"异常安全性"的问题:试想,假设在new的时候抛出了一个异常(假设内存不足),那么,a在处理异常时,arr的状态就已经发生变化了。
另外,书中介绍了另一种避免赋值的时候释放掉有用内存的代码:
#include <iostream>
class A {
private:
int
*arr;
public:
A()
{
arr =
new
int[256];
}
~A()
{
delete arr;
}
const A&
operator=(const A &other)
{
int
*old_arr = arr;
arr =
new
int[256];
std::memcpy(arr, other.arr,
256
*
sizeof(int));
delete old_arr;
return
*this;
}
};
这段代码中,先对原有的arr做一个备份,然后使用other对新分配的内存进行更新,最后释放掉原来arr指向的内存区域。
即使没有"证同测试",这段代码也能正常工作,因为释放动作在赋值动作之后,这是后就真的存在两个副本了(如果*this和other指向不同的值,就是3个副本)。但是,这段代码显然在抛开"异常安全性"后在效率上比上面那段代码的效率低(即使两个对象指向同一内存,也要从新分配内存,并重新赋值)。所以,如果关心效率的话,应该在最前面增加"证同测试"。如下:
#include <iostream>
class A {
private:
int
*arr;
public:
A()
{
arr =
new
int[256];
}
~A()
{
delete arr;
}
const A&
operator=(const A &other)
{
if
(this==&other)
return
*this;
int
*old_arr = arr;
arr =
new
int[256];
std::memcpy(arr, other.arr,
256
*
sizeof(int));
delete old_arr;
return
*this;
}
};
至此,一份"异常安全的"且"效率优秀的"operator=操作符就完成了
请记住:
- 确保operator=有良好的行为。
- 当某个函数要操作同类对象或者多个继承自同一类对象时,不仅要考虑每个对象不同时的处理,还要考虑当某些对象是同一个对象的处理,确保正确性。
EC笔记:第二部分:11:在operator=中处理“自我赋值”的更多相关文章
- EC读书笔记系列之6:条款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/) 为什么会出现自我赋值呢?不明显的自我赋值,是“别名”带来的结果: ...
- Effective C++ -----条款11: 在operator=中处理“自我赋值”
确保当对象自我赋值时operator=有良好行为.其中技术包括比较“来源 对象”和“目标对象”的地址.精心周到的语句顺序.以及copy-and-swap. 确定任何函数如果操作一个以上的对象,而其中多 ...
- 11——在operator=中处理自我赋值
在operator=函数中加一个测试: if(&rhs==this) copy and swap
- 条款11:在operator=中处理“自我赋值”
什么是自我赋值,就是 v = v 这种类型的语句,也许很多人都会说鄙视这种写法,但是如下的写法会不会出现呢? 比如:a[i] = a[j]; // 不巧的是i可能和j相等 *px = *py ...
- 【11】在operator=中处理“自我赋值”
1.自我赋值,看起来愚蠢,但是却合法.有些自我赋值一眼就可看出来.有些自我赋值是潜在的.比如:a[i] = a[j]; *px = *py; 甚至不同类型的指针,都指向同一个地址,也是自我赋值,这一类 ...
- Effective C++ 条款11,12 在operator= 中处理“自我赋值” || 复制对象时不要忘记每一个成分
1.潜在的自我赋值 a[i] = a[j]; *px = *py; 当两个对象来自同一个继承体系时,他们甚至不需要声明为相同类型就可能造成别名. 现在担心的问题是:假如指向同一个对象, ...
- Effective C++ 条款11:在operator=中处理"自我赋值"
"自我赋值"发生在对象被赋值给自己时: class Widget { ... }; Widget w; ... w = w; // 赋值给自己 a[i] = a[j]; // 潜在 ...
- [Effective C++ --011]在operator=中处理“自我赋值”
一.何谓“自我赋值”? 1.1.场合一 直接赋值 w = w; 1.2.场合二 同一数组 a[i] = a[j]: 1.3.场合三 指针 *px = *py: 1.4. ...
随机推荐
- [公告][重要]Senparc.Weixin v4.9.0 & Senparc.Weixin.MP v14.3.104更新说明
本次升级除了更新了发红包接口等接口之外,最重要的是重构了缓存模块. 如何升级? 之前的缓存是为Container设计的,原先的ContainerCacheStrategy继承自BaseCacheStr ...
- 每天一个linux命令(23):Linux 目录结构
对于每一个Linux学习者来说,了解Linux文件系统的目录结构,是学好Linux的至关重要的一步.,深入了解linux文件目录结构的标准和每个目录的详细功能,对于我们用好linux系统只管重要,下面 ...
- java即时通信小例子
学习java一段时间了,今天写来一个即时通信的小例子练手在其过程中也学到了一些知识拿出来和大家分享,请路过的各位大神多多赐教... 好了下面讲一下基本的思路: 首先,编写服务器端的程序,简单点说吧就是 ...
- RequireJS与Backbone简单整合
前言 昨天我们一起学习了Backbone,最后做了一个备忘录的例子,说是做了不如说是看了下官方提供的例子,所以最终我感觉我们还是没能掌握Backbone,今天还得做个其它例子先. 然后前面也只是草草学 ...
- SSIS Design1: 源数据提取
数据量的大小由两个方面决定:行的宽度和数据行的数量,为了减少ETL运行的时间,可以从源数据的提取上做优化,从数据源的输入上控制数据的质量和大小,减少转换和IO. 一,减少行的宽度 1,只加载需要的数据 ...
- Entity Framework 6 Code First新特性:支持存储过程
Entity Framework 6提供支持存储过程的新特性,本文具体演示Entity Framework 6 Code First的存储过程操作. Code First的插入/修改/删除存储过程 默 ...
- 那些让IE6-8羞愧的替补型js
1,html5shiv 这个js特别简单,可以让IE8识别一些新的标签,常用的比如 header footor section,就能使用更好的语义的标签了. 引入方式: <!--[if lt I ...
- Android混淆代码
Android代码混淆是必须的,java层代码如果不做混淆等于把源代码送人了.那如何做混淆呢? 之前一般都是提到采用proguard.cfg,但使用新版本ADT后没有这个文件了,取而代之的是progu ...
- String,StringBuffer与StringBuilder的区别??
转自http://blog.csdn.net/rmn190/article/details/1492013 String 字符串常量 StringBuffer 字符串变量(线程安全) StringBu ...
- Android数据存储之Sqlite的介绍及使用
前言: 本来没有打算整理有关Sqlite数据库文章的,最近一直在研究ContentProvider的使用,所有觉得还是先对Sqlite进行一个简单的回顾,也方便研究学习ContentProvider. ...