已经一年半没有写过博客了,最近发现学过的知识还是需要整理一下,为知笔记,要开始收费了以前写在为知笔记上笔记也会慢慢的转到博客里。

话不多说,进入正题。

考虑考虑以下场景:

当某个对象对自身赋值时,会出现什么现象??

例子:

#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=操作符就完成了

请记住:

  1. 确保operator=有良好的行为。
  2. 当某个函数要操作同类对象或者多个继承自同一类对象时,不仅要考虑每个对象不同时的处理,还要考虑当某些对象是同一个对象的处理,确保正确性。

EC笔记:第二部分:11:在operator=中处理“自我赋值”的更多相关文章

  1. EC读书笔记系列之6:条款11 在operator=中处理自我赋值

    记住: ★确保当对象自我赋值时operator=有良好行为.有三种方法:比较“来源对象”和“目标对象”的地址.精心周到的语句顺序.以及copy-and-swap技术 ★确定任何函数若操作一个以上对象, ...

  2. 读书笔记 effective c++ Item 11 在operator=中处理自我赋值

    1.自我赋值是如何发生的 当一个对象委派给自己的时候,自我赋值就会发生: class Widget { ... }; Widget w; ... w = w; // assignment to sel ...

  3. Effective C++_笔记_条款11_在operator=中处理“自我赋值”

    (整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 为什么会出现自我赋值呢?不明显的自我赋值,是“别名”带来的结果: ...

  4. Effective C++ -----条款11: 在operator=中处理“自我赋值”

    确保当对象自我赋值时operator=有良好行为.其中技术包括比较“来源 对象”和“目标对象”的地址.精心周到的语句顺序.以及copy-and-swap. 确定任何函数如果操作一个以上的对象,而其中多 ...

  5. 11——在operator=中处理自我赋值

    在operator=函数中加一个测试: if(&rhs==this) copy and swap

  6. 条款11:在operator=中处理“自我赋值”

    什么是自我赋值,就是 v = v 这种类型的语句,也许很多人都会说鄙视这种写法,但是如下的写法会不会出现呢? 比如:a[i] = a[j];      // 不巧的是i可能和j相等 *px = *py ...

  7. 【11】在operator=中处理“自我赋值”

    1.自我赋值,看起来愚蠢,但是却合法.有些自我赋值一眼就可看出来.有些自我赋值是潜在的.比如:a[i] = a[j]; *px = *py; 甚至不同类型的指针,都指向同一个地址,也是自我赋值,这一类 ...

  8. Effective C++ 条款11,12 在operator= 中处理“自我赋值” || 复制对象时不要忘记每一个成分

    1.潜在的自我赋值     a[i] = a[j];     *px = *py; 当两个对象来自同一个继承体系时,他们甚至不需要声明为相同类型就可能造成别名. 现在担心的问题是:假如指向同一个对象, ...

  9. Effective C++ 条款11:在operator=中处理"自我赋值"

    "自我赋值"发生在对象被赋值给自己时: class Widget { ... }; Widget w; ... w = w; // 赋值给自己 a[i] = a[j]; // 潜在 ...

  10. [Effective C++ --011]在operator=中处理“自我赋值”

    一.何谓“自我赋值”? 1.1.场合一 直接赋值 w = w; 1.2.场合二 同一数组         a[i] = a[j]: 1.3.场合三 指针         *px = *py: 1.4. ...

随机推荐

  1. 慎重管理SQL Server服务的登录(启动)账户和密码

    今天是大年初三,先跟大家拜个年,祝大家新年快乐.今天处理了一个alwaysOn问题——辅助副本因为磁盘空间不足一直显示[未同步——可疑],在日志中可以看到数据库处于挂起状态,与主副本失去同步.原以为只 ...

  2. ASP.NET MVC 路由(一)

    ASP.NET MVC路由(一) 前言 从这一章开始,我们即将进入MVC的世界,在学习MVC的过程中在网上搜索了一下,资料还是蛮多的,只不过对于我这样的初学者来看还是有点难度,自己就想看到有一篇引导性 ...

  3. [大数据之Sqoop] —— 什么是Sqoop?

    介绍 sqoop是一款用于hadoop和关系型数据库之间数据导入导出的工具.你可以通过sqoop把数据从数据库(比如mysql,oracle)导入到hdfs中:也可以把数据从hdfs中导出到关系型数据 ...

  4. Java异常内容总结

    在程序开发中,可能存在各种错误,有些错误是可以避免的,而有些错误却是意想不到的,在Java中把这些可能发生的错误称为异常. Throwable类是所有异常类的超类,该类的两个直接子类是Error和Ex ...

  5. Atitit 代码复用的理解attilax总结

    Atitit 代码复用的理解attilax总结 1.1. 继承1 1.1.1. 模式1:原型继承1 1.1.2. 模式2:复制所有属性进行继承 拷贝继承1 1.1.3. 模式3:混合(mix-in)1 ...

  6. JavaEE:JavaEE技术组成

    Java平台版本: JavaSE:Java Platform Standard Edition,标准版,用来开发桌面应用系统: JavaEE:Java Plateform Enterprise Edi ...

  7. OLE DB Command transformation 用法

    OLE DB Command transformation component 能够引用参数,逐行调用sqlcommand,This transformation is typically used ...

  8. UITableView 一直显示滚动条(ScrollBar Indicators)、滚动条Width(宽度)、滚动条Color(颜色)

    在 IOS 中,对 UIScrollView 的滚动条(ScrollBar Indicators)的自定义设置接口,一直都是很少的.除了能自定义简单的样式(UIScrollViewIndicatorS ...

  9. Dnsmasq安装与配置

    默认的情况下,我们平时上网用的本地DNS服务器都是使用电信或者联通的,但是这样也导致了不少的问题,首当其冲的就是上网时经常莫名地弹出广告,或者莫名的流量被消耗掉导致网速变慢.其次是部分网站域名不能正常 ...

  10. Windows系统盘占用空间分析

    磁盘分析 本机的系统盘是C盘,操作系统是Windows 7 专业版,通过磁盘属性可以看到C盘的已用空间是69.4G. 而我们运行自己编写的脚本(脚本程序参考附录,统计原理:计算目录下各个文件的大小,然 ...