在operator=中处理“自我赋值”——条款11

2023-11-07

“自我赋值”发生在对象被赋值给自己时:

class Widget {...};

Widget w;

...

w = w; //赋值给自己

    这看起来有点愚蠢,但它合法,所以不要认定客户绝不会那么做。此外赋值动作并不总是那么可被一眼辨识出来,例如:

    a[i] = a[j];  //潜在的自我赋值

    如果i和j有相同的值,这便是自我赋值。再看:

    *px = *py;  //潜在的自我赋值

    如果px和py恰巧指向同一个东西,这也是自我赋值。

    自我赋值有可能会掉进“在停止使用资源之前以外释放了它”的陷阱。假设你建立一个class用来保存一个指针指向一块动态分配的位图(bitmap):

class Bitmap {...};
class Widget {
    ...
private:
    Bitmap* bp;
};

下面是operator=实现代码,表面上看起来合理,但自我赋值出现时并不安全(它也不具备异常安全性,但我们稍后才讨论这个主题)。

Widget& Widget::operator=(const Widget& rhs)
{
    delete pb;
    pb = new Bitmap(*rhs.pb);
    return *this;
}

       这里的自我赋值问题是,operator=函数内的*this(赋值的目的端)和rhs有可能是同一个对象。果真如此delete就不只是销毁当前对象的bitmap,它也销毁rhs的bitmap。在函数末尾,Widget——它原本就不该被自我赋值动作改变的——发现自己持有一个指针指向一个已被删除的对象!

        欲阻止这种错误,传统做法是藉由operator=最前面的一个“证同测试(identity test)”达到“自我赋值”的检测目的:

Widget& Widget::operator=(const Widget& rhs)
{
    if (this == &rhs) return *this;  //证同测试:如果是自我赋值,就不做任何事。
    delete pb;
    pb = new Bitmap(*rhs.pb);
    return *this;
}

        这样做行得通。但是如果“new Bitmap”导致异常(不论是因为分配时内存不足或因为Bitmap的copy构造函数抛出异常),Widget最终会持有一个指针指向一块被删除的Bitmap。

解决方法:

Widget& Widget::operator=(const Widget& rhs)
{
    Bitmap* pOrig = pb;        // 记住原先的pb
    pb = new Bitmap(*rhs.pb);  // 令pb指向*pb的一个副本
    delete pOrig;              // 删除原先的pb
    return *this;
}

        现在,如果“new Bitmap”抛出异常,pb(及其栖身的那个Widget)保持原状。即使没有证同测试,这段代码还是能够处理自我赋值,因为我们对原bitmap做了一份复件、删除原bitmap、然后指向新制造的那个复件。它或许不是处理“自我赋值”的最高效办法,但它行得通。

        在operator=函数内手工排列语句(确保代码不但“异常安全”而且“自我赋值安全”)的一个替代方案是,使用所谓的copy and swap技术。这个技术和“异常安全性”有密切关系,所有由条款29详细说明。然而由于它是一个常见而够好的operator=撰写办法,所以值得看看其实现手法像什么样子:

class Widget {
    ...
    void swap(Widget& rhs);  // 交换*this和rhs的数据;详见条款29
    ...
};

Widget& Widget::operator=(const Widget& rhs)
{
    Widget temp(rhs);  // 为rhs数据制作一份副本
    swap(temp);        // 将*this数据和上述附件的数据交换。
    return *this;
}

请记住

  • 确保当前对象自我赋值时operator=有良好的行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap。

  • 确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在operator=中处理“自我赋值”——条款11 的相关文章

  • Swift语言精要 - Operator(运算符重载)

    运算符重载 Swift的这一语言特性或许应该启发于C 43 43 class Vector2D var x Float 61 0 0 var y Float 61 0 0 init x Float y Float self x 61 x s
  • 自定义prometheus-operator中容器的参数

    简介 prometheus opertator 方便了我们在Kubernetes里面部署prometheus集群 xff0c 但是有一些底层的配置也被掩盖了 我遇到的问题就是配置文件的实时生效 xff0c 要等3分钟才可以 于是去翻了一下C
  • [错误分析][Error]no match for ‘operator<<‘无匹配的左移运算符

    错误提示 xff1a Error no match for operator lt lt operand types are std basic ostream and plural 先看一下可能引发此错误的代码 span class to
  • C++调试报错 no match for operator...operand types

    在调试C 43 43 代码遇到一个小错误 xff0c 但是也比较常见 xff0c 报错如下 xff1a error span class token operator span no match span class token keywo
  • 考虑写出一个不抛异常的swap函数——条款25

    swap是个有趣的函数 原本它只是STL的一部分 而后成为异常安全性编程 exception safe programing 见条款29 的脊柱 以及用来处理自我赋值可能性 见条款11 的一个常见机制 由于swap如此有用 适当的实现很重要
  • python——operator详解

    1 概述 operator模块是python中内置的操作符函数接口 它定义了一些算术和比较内置操作的函数 operator模块是用c实现的 所以执行速度比python代码快 2 函数的映射操作 操作 语法 函数 加法 a b add a b
  • 透彻了解inlining的里里外外——条款30

    Inline函数 多棒的点子 它们看起来像函数 动作像函数 比宏好得多 见条款2 可以调用它们又不需要蒙受函数调用所招致的额外开销 你还能要求更多吗 你实际获得的比想到的还多 因为 免除函数调用成本 只是故事的一部分而已 编译器最优化机制通
  • 《Effective C++》学习笔记——区分接口继承和实现继承

    派生类public继承自基类 其中函数均是接口继承 实现继承又分为缺省继承与强制继承 对应着虚函数与非虚函数 我们在做项目时 对于任何事物都要抱有先描述再组织的心态 因此 当描述事物为其构建框架尤其是存在继承 is a 关系时 一定要搞清楚
  • 令operator=返回一个reference to *this——条款10

    关于赋值 有趣的是你可以把它们写成连锁形式 int x y z x y z 15 同样有趣的是 赋值采用右结合律 所以上述连锁赋值被解析为 x y z 15 这里15先被赋值给z 然后其结果 更新后的z 再被赋值给y 然后其结果 更新后的y
  • 尽可能延后变量定义式的出现时间——条款26

    只要你定义了一个变量而其类型带有一个构造函数或析构函数 那么当程序控制流 control flow 到达这个变量定义式时 你便得承受构造成本 当这个变量离开其作用域时 你便得承受析构成本 即使这个变量最终未被使用 仍需耗费这些成本 所以你应
  • 在operator=中处理“自我赋值”——条款11

    自我赋值 发生在对象被赋值给自己时 class Widget Widget w w w 赋值给自己 这看起来有点愚蠢 但它合法 所以不要认定客户绝不会那么做 此外赋值动作并不总是那么可被一眼辨识出来 例如 a i a j 潜在的自我赋值 如
  • 尽量以const、enum、inline替换 #define——条款02

    这个条款或许改为 宁可以编译器替换预处理器 比较好 因为或许 define 不能被视为语言的一部分 一 比如定义一个宏 define ASPECT RATIO 1 653 这个ASPECT RATIO也许从未被编译器看见 也许在编译器开始处
  • 条款13: 以对象管理资源

    结论 为防止资源泄漏 请使用RAII对象 它们在构造函数中获得资源并在析构函数中释放资源 两个常被使用的RAII classes分别是tr1 share ptr和auto ptr 前者通常是较佳选择 因为其copy行为比较直观 若选择aut
  • Effective C++改善程序与设计的55个具体做法笔记

    Scott Meyers大师Effective三部曲 Effective C More Effective C Effective STL 这三本书出版已很多年 后来又出版了Effective Modern C More Effective
  • 《Effective C++》 全书内容提炼总结

    个人博客地址 https cxx001 gitee io 本文阅读说明 孔子云 取乎其上 得乎其中 取乎其中 得乎其下 取乎其下 则无所得矣 对于读书求知而言 这句古训教我们去读好书 最好是好书中的上品 经典书 Effective C 就是
  • 将与参数无关的代码抽离templates——条款44

    Templates是节省时间和避免代码重复的一个奇方妙法 不再需要键入20个类似的classes而每一个带有15个成员函数 你只需键入一个class template 留给编译器去具现化那20个你需要的相关classes和300个函数 cl
  • 考虑virtual函数以外的其他选择——条款35

    假设你正在写一个视频游戏软件 你打算为游戏内的人物设计一个继承体系 你的游戏术语暴力砍杀类型 剧中人物被伤害或因其他因素而降低健康状态的情况并不罕见 你因此决定提供一个成员函数healthValue 它会返回一个整数 表示人物的健康程度 由
  • 怎样为std::map的自定义key提供比较操作(一)

    stl的关联容器 map set 的key一般要求提供 lt 比较操作 假设我们有一个结构SomeKey struct SomeKey int a b 要想以SomeKey作为std map的key 需要为这个结构提供operator lt
  • 掌握 Effective C++ : 条款01

    背景 Effective C 是每个 C 程序员都应该读的经典之作 书中涵盖了 C 编程中的一系列最佳实践 包括了面向对象设计 模板 STL 异常处理等方面的内容 由于 C 的发展非常迅速 书中的某些内容可能已经过时 但依然是值得好好学习的
  • 复制对象时勿忘其每一个成分——条款12

    设计良好之面向对象系统 OO systems 会将对象的内部封装起来 只留两个函数负责对象拷贝 复制 那便是带着适切名称的copy构造函数和copy assignment操作符 我称它们为copying函数 条款5观察到编译器会在必要的时候

随机推荐

  • Spring中有哪些情况会导致@Bean注入失效呢?

    转自 Spring中有哪些情况会导致 Bean注入失效呢 下文笔者讲述Spring中导致 bean注入失败的相关情况说明 如下所示 实现思路 1 component scan扫描路径配置错误 2 Conditional修饰条件 3 bean
  • 学习swift的资料

    https developer apple com library content documentation Swift Conceptual Swift Programming Language CollectionTypes html
  • 数据库表创建索引

    创建一个表 创建名为 Person 的表 有四个列 列名是 LastName FirstName Address 以及 Age 定义列的长度 CREATE TABLE Person LastName varchar 30 FirstName
  • 使用scrapy框架爬取51job的关于python的职位,并且进行分析

    example py 爬虫的主文件 大部分的爬虫逻辑都在这 coding utf 8 import scrapy import pyecharts from items import Scrapy3Item class ExampleSpi
  • cpp: Strategy Pattern

    Gold h 此文件包含 Gold 类 策略模式 Strategy Pattern C 14 2023年5月1日 涂聚文 Geovin Du Visual Studio 2022 edit pragma once ifndef GOLD H
  • MySql中的数据修改

    1 insert插入操作 单行插入的语法格式 INSERT INTO student 列表1 列表2 列表3 列表4 列表5 列表6 VALUES 值1 值1 值1 值1 值1 值1 INSERT INTO student id name
  • python赋值、深拷贝和浅拷贝的区别详解

    一 前言 在python中 对象赋值实际上是对象的引用 当创建一个对象 然后把它赋值给另一个变量的时候 python并没有拷贝这个对象 而只是拷贝了这个对象的引用 二 区别 1 直接赋值 默认浅拷贝传递对象的引用而已 原始列表改变 被赋值的
  • 使用企业微信登录小程序

    概述 当小程序在企业微信端运行时 需要通过对应的登录接口获取到当前企业微信用户在当前企业的员工身份信息 开发者需特别关注 当小程序在微信端运行时由微信派发和验证code参数 当小程序在企业微信端运行时由企业微信派发和验证code参数 两个平
  • vue+element-ui实现一键切换皮肤

    element ui可以自己定义主题并下载 选择好自己想要的主题 下载到本地 我下载了一套暗黑模式 一套默认的用来白天黑夜模式切换 文件目录如下 在项目的index html文件中 在sideBar vue页面中 新增下拉框选择模式
  • 快速搭建TP6-02

    快速搭建TP6 02 1 配置多应用config app php return 应用地址 app host gt env app host 应用的命名空间 app namespace gt 是否启用路由 with route gt true
  • 浅谈分布式系统 - 架构演进

    目录 1 架构演进 1 1 单机架构 1 2 什么是分布式架构 1 3 数据库和应用分离 1 4 引入负载均衡 1 5 引入数据库读写分离 1 6 引入缓存 1 7 数据库分库分表 1 8 微服务架构 2 分布式系统下的常见概念 1 架构演
  • RDA EQ&频响曲线

    相关数据 FAC gt Audio gt EQ Setting EQ Band 1 7 Gain 0 Frequency 500 Q Factor 1 5 FAC gt Audio gt PEQ 1 2 3 Enable Enable Ce
  • 使用代理进行爬虫

    爬网页的时候 尤其是一些商用网站 如果使用本地IP很容易就会被封掉 因此我们需要在代理网站上购买代理 我使用的是代理精灵网站 http http zhiliandaili com Users login html 首先要在IP白名单中加入自
  • 美赛python学习d4--python在高等数学和线性代数中的应用

    科学计算设计数值计算和符号计算 在python中作基础数值计算用numpy和scipy工具库 作符号运算用sympy工具库 sympy工具库 符号运算 sympy常见模块 符号运算基本知识 利用symbols函数创建符号变量 构造多个符号变
  • 使用ffmpeg时出现undefined reference to `lzma_stream_decoder‘的错误解决

    解决方法 加上 llzma选项
  • 基于微前端qiankun的多页签缓存方案实践

    作者 vivo 互联网前端团队 Tang Xiao 本文梳理了基于阿里开源微前端框架qiankun 实现多页签及子应用缓存的方案 同时还类比了多个不同方案之间的区别及优劣势 为使用微前端进行多页签开发的同学 提供一些参考 一 多页签是什么
  • PyCharm远程开发指南(三)- 从PyCharm连接到远程服务器

    本文译自PyCharm 2022 2官方文档 考虑到当前疫情形势下远程工作已不可或缺 PyCharm提供了远程开发的功能来帮助团队远程编码 运行 调试和部署项目 前提 在开始在远程机上开发之前 确认满足一下条件 远程机 IDE 满足最低推荐
  • 删除微信代码管理(git)分支

    1 终端打开 2 执行命令 git push cfg 远程名称 delete sd 分支名称 会提示输入git的用户名和密码 注意默认分支删除不了
  • 2021-08-04

    标签属性详解 HTML中html标签的作用 1 标签 效果 声明是文档中的第一成分 坐落标签之前 2 标签 效果 此元素可告知浏览器其自身是一个HTML文档 特点 manifest 值 url 为脱机运用界说缓存信息 3 标签 效果 标签用
  • 在operator=中处理“自我赋值”——条款11

    自我赋值 发生在对象被赋值给自己时 class Widget Widget w w w 赋值给自己 这看起来有点愚蠢 但它合法 所以不要认定客户绝不会那么做 此外赋值动作并不总是那么可被一眼辨识出来 例如 a i a j 潜在的自我赋值 如