[翻译] Effective C++, 3rd Edition, Item 11: 在 operator= 中处理 assignment to self(自赋值)

8155平台multi-display(多屏显示)原理与配置 8155平台多屏互动原理与配置 阅读详情

Item 11: 在 operator= 中处理 assignment to self(自赋值)

作者:Scott Meyers

译者:fatalerror99 (iTePub's Nirvana)

发布:http://blog.youkuaiyun.com/fatalerror99/

当一个 object(对象)赋值给自己的时候就发生了一次 assignment to self(自赋值):

class Widget { ... };

Widget w;
...

w = w;                                   // assignment to self

这看起来很愚蠢,但它是合法的,所以应该确信客户会这样做。另外,assignment(赋值)也并不总是那么容易辨别。例如,

a[i] = a[j];                             // potential assignment to self

如果 i 和 j 有同样的值就是一个 assignment to self(自赋值),还有

*px = *py;                               // potential assignment to self

如果 px 和 py 碰巧指向同一个东西,这也是一个 assignment to self(自赋值)。这些不太明显的 assignments to self(自赋值)是由 aliasing(别名)(有不止一个方法引用一个 object(对象))造成的。通常,使用 references(引用)或者 pointers(指针)操作相同类型的多个 objects(对象)的代码需要考虑那些 objects(对象)可能相同的情况。实际上,如果两个 objects(对象)来自同一个 hierarchy(继承体系),甚至不需要声明为相同的类型,因为一个 base class(基类)的 reference(引用)或者 pointer(指针)也能够引向或者指向一个 derived class(派生类)类型的 object(对象):

class Base { ... };

class Derived: public Base { ... };

void doSomething(const Base& rb,                   // rb and *pd might actually be
                 Derived* pd);                     // the same object

如果你遵循 Items 13 和 14 的建议,你应该总是使用 objects(对象)来管理 resources(资源),而且你应该确保那些 resource-managing objects(资源管理对象)被拷贝时行为良好。在这种情况下,你的 assignment operators(赋值运算符)在你没有考虑自赋值的时候可能也是 self-assignment-safe(自赋值安全)的。然而,如果你试图自己管理 resources(资源)(如果你正在写一个 resource-managing class(资源管理类),你当然必须这样做),你可能会落入在你用完一个 resource(资源)之前就已意外地将它释放的陷阱。例如,假设你创建了一个 class(类),它持有一个指向动态分配 bitmap(位图)的 raw pointer(裸指针):

class Bitmap { ... };

class Widget {
  ...

private:
  Bitmap *pb;                                     // ptr to a heap-allocated object
};

下面是一个表面上看似合理 operator= 的实现,但如果出现 assignment to self(自赋值)则是不安全的。(它也不是 exception-safe(异常安全)的,但我们要过一会儿才会涉及到它。)

Widget&
Widget::operator=(const Widget& rhs)              // unsafe impl. of operator=
{
  delete pb;                                      // stop using current bitmap
  pb = new Bitmap(*rhs.pb);                       // start using a copy of rhs's bitmap

  return *this;                                   // see Item 10
}

这里的 self-assignment(自赋值)问题在 operator= 的内部,*this(赋值的目标)和 rhs 可能是同一个 object(对象)。如果它们是,则那个 delete 不仅会销毁 current object(当前对象)的 bitmap(位图),也会销毁 rhs 的 bitmap(位图)。在函数的结尾,Widget ——通过 assignment to self(自赋值)应该没有变化——发现自己持有一个指向已删除 object(对象)的指针!

防止这个错误的传统方法是在 operator= 的开始处通过 identity test(一致性检测)来阻止 assignment to self(自赋值):

Widget& Widget::operator=(const Widget& rhs)
{
  if (this == &rhs) return *this;   // identity test: if a self-assignment,
                                    // do nothing
  delete pb;
  pb = new Bitmap(*rhs.pb);

  return *this;
}

这个也能工作,但是我在前面提及那个 operator= 的早先版本不仅仅是 self-assignment-unsafe(自赋值不安全)的,它也是 exception-unsafe(异常不安全)的,而且这个版本还有异常上的麻烦。详细地说,如果 "new Bitmap" 表达式引发一个 exception(异常)(可能因为供分配的内存不足或者因为 Bitmap 的 copy constructor(拷贝构造函数)抛出一个异常),Widget 将以持有一个指向被删除的 Bitmap 的指针而告终。这样的指针是有毒的,你不能安全地删除它们。你甚至不能安全地读取它们。你对它们唯一能做的安全的事情大概就是花费大量的调试精力来断定它们起因于哪里。

幸亏,使 operator= exception-safe(异常安全)一般也同时弥补了它的 self-assignment-safe(自赋值安全)。这就导致了更加通用的处理 self-assignment(自赋值)问题的方法就是忽略它,而将焦点集中于达到 exception safety(异常安全)。Item 29 更加深入地探讨了 exception safety(异常安全),但是在本 Item 中,已经足以看出,在很多情况下,仔细地调整一下语句的顺序就可以得到 exception-safe(异常安全)(同时也是 self-assignment-safe(自赋值安全))的代码。例如,在这里,我们只要注意不要删除 pb,直到我们拷贝了它所指向的目标之后:

Widget& Widget::operator=(const Widget& rhs)
{
  Bitmap *pOrig = pb;               // remember original pb
  pb = new Bitmap(*rhs.pb);         // make pb point to a copy of *pb
  delete pOrig;                     // delete the original pb

  return *this;
}

现在,如果 "new Bitmap" 抛出一个 exception(异常),pb(以及它所在的 Widget)的遗迹没有被改变。甚至不需要 identity test(一致性检测),这里的代码也能处理 assignment to self(自赋值),因为我们做了一个原始 bitmap(位图)的拷贝,删除原始 bitmap(位图),然后指向我们作成的拷贝。这可能不是处理 self-assignment(自赋值)的最有效率的做法,但它能够工作。

如果你关心效率,你可以在函数开始处恢复 identity test(一致性检测)。然而,在这样做之前,先问一下自己,你认为 self-assignments(自赋值)发生的频率是多少,因为这个检测不是免费午餐。它将使代码(源代码和目标代码)有少量增大,而且它将在控制流中引入一个分支,这两点都会降低运行速度。例如,instruction prefetching(指令预读),caching(缓存)和 pipelining(流水线操作)的效力都将被降低。

另一个可选的手动排列 operator= 中语句顺序以确保实现是 exception- and self-assignment-safe(异常和自赋值安全)的方法是使用被称为 "copy and swap" 的技术。这一技术和 exception safety(异常安全)关系密切,所以将在 Item 29 中描述。然而,这是一个写 operator= 的足够通用的方法,值得一看,这样一个实现看起来通常就像下面这样:

class Widget {
  ...
  void swap(Widget& rhs);       // exchange *this's and rhs's data;
  ...                           // see Item 29 for details
};

Widget& Widget::operator=(const Widget& rhs)
{
  Widget temp(rhs);             // make a copy of rhs's data

  swap(temp);                   // swap *this's data with the copy's
  return *this;
}

在这个主题上的一个变种利用了如下事实:(1)一个 clsaa(类)的 copy assignment(拷贝赋值运算符)可以被声明为 take its argument by value(以传值方式取得它的参数);(2)通过传值方式传递某些东西以做出它的一个 copy(拷贝)(参见 Item 20):

Widget& Widget::operator=(Widget rhs)   // rhs is a copy of the object
{                                       // passed in — note pass by val

  swap(rhs);                            // swap *this's data with
                                        // the copy's
  return *this;
}

对我个人来说,我担心这个方法在灵活的祭坛上牺牲了清晰度,但是通过将拷贝操作从函数体中转移到参数的构造中,有时能使编译器产生更有效率的代码倒也是事实。

Things to Remember

  • 当一个 object(对象)被赋值给自己的时候,确保 operator= 是行为良好的。技巧包括比较 source(源)和 target objects(目标对象)的地址,关注语句顺序,和 copy-and-swap。
  • 如果两个或更多 objects(对象)相同,确保任何操作多于一个 object(对象)的函数行为正确。
ISCC-Amy‘s Code CTF-Reverse ISCC-Amy's Code CTF-Reverse 阅读详情

相关推荐

别再到处找资源了!WinCC 7.5 SP2官方下载与Windows 10保姆级安装避坑指南

本文提供了WinCC 7.5 SP2在Windows 10上的官方下载与安装全流程指南,重点解决了资源获取难、系统兼容性问题及分卷压缩处理等常见痛点。通过详细的步骤和实用技巧,帮助用户顺利完成安装并优化配置,适用于工控领域的学习者和从业者。

weixin_32924297的博客 493

C++笔记 11:在operator中处理“自我赋值“

Effective C++笔记 Handle assignment to self in operator= 别名 先调用delete 证同测试(identity test) 异常安全性(exception safety) copy and swap 总结

qq_36333986的博客 711

HPM6E00:PWM V2使用指南

HPM6E00:PWM V2使用指南

xikangsoon的博客 1734

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

通常我们在设计一个类并为它声明了赋值操作符时有一种状况总是需要处理——自赋值,如果用户在代码中注意避免自赋值当然可以但是那是很理想的状况,在一些复杂的情况下用户自己可能都不会意识到做了“自赋值”这个操作。例如:      *px=*py;//潜在的自我赋值       如果px和py恰巧指向同一个东西,这也是自我赋值。这些并不明显的自我赋值,是“别名”带来的结果。 我们来看一段代码: cl

qq_31984717的博客 413

SAP 小结

关于SAP 开发的实习就要告一段落了,从刚开始接触,到现在已经6个月了,想要很简要的把自己对SAP的理解写出来,便于以后查阅(不定期更新)。 1. 什么是SAP? 广义的SAP,可以指代SAP公司以及SAP的各种产品,它的意义要在特定语境下才能确定。一般我们所说的SAP特指SAP ERP。 SAP ERP(enterprise resource planning)是一个企业信息系统。通过这个系

Remy的博客 1955

鼎捷T100二次开发实战:如何给开窗功能加上权限锁(附完整代码)

本文详细解析了鼎捷T100二次开发中开窗功能权限锁的实现方法,提供了两种技术方案:开窗程序重构法和动态WHERE条件注入法,并附上完整代码示例。通过真实案例,帮助开发者解决数据权限细粒度控制的难题,提升ERP系统的安全性和实用性。

weixin_28454475的博客 443

20230103编译ToyBrick的TB-RK3588X的Andorid12的LOG07

20230103编译ToyBrick的TB-RK3588X的Andorid12的LOG07。编译环境:Ubuntu18.04.6 64位。

南岭笑笑生之家 443

Pure Persuit算法基础

Pure Persuit算法本身的原理不难理解,它主要基于几何原理进行的设备控制,本身不考虑太多的运动学约束。同时机器人的路径跟踪效果应该会比较依赖于预瞄点。对于曲线部分而言,如果曲率很小而预瞄点很远的话,对于曲线的跟随性应该会很差。对于直线而言,如果设备在开始时与轨迹有横向偏差,则在运动过程中机器人会逐渐收敛到曲线,但是收敛速度取决于预瞄点距离。

YiYeZhiNian的博客 1522

条款11(二):在operator= 中处理“自我赋值”

异常安全性&自我赋值安全性、Copy&Swap

不积跬步无以至千里 704

【OpenCV】图像的像素直方图 图像特征点 cv::FAST cv::ORB ORB_SLAM2::ORB

文章目录图像直方图代码实现cv::FAST和cv::ORB 检测图像特征点cv::FAST和cv::ORB 检测图像特征点代码实现ORB_SLAM2::ORB和自己实现ORB图像特征点检测效果对比ORB_SLAM2::ORB和自己实现ORB图像特征点检测效果对比代码实现 图像直方图代码实现 图像是由不同数值(颜色)的像素构成的,像素值在图像中的分布情况是图像的重要属性,可以用直方图修改图像的外观,标识图像的内容,检测图像中特定的物体或纹理。 单通道灰度图像中每个像素都有一个 0(黑色)-255(白色)的整

小秋 AI SLAM 入门实战教程 2510

UI自动化控制PC端微信获取当前群聊的群友列表

在一个月前,某个群友在获取整个群的群友信息时遇到了一点问题: 对整个群进行群友数据获取经测试确实有点棘手,下面我将我的解决过程公布给大家。 基础教程详见: https://blog.youkuaiyun.com/as604049322/article/details/121391639 人工打开要抓取的群聊窗口后,首先获取微信窗口并点击聊天信息按钮: import uiautomation as auto auto.uiautomation.SetGlobalSearchTimeout(1) wechatWind

小小明-代码实体的专栏 3万+

Effective C++之 Item 11: 在 operator= 中处理 assignment to self(自赋值)

当一个 object(对象)赋值给自己的时候就发生了一次 assignment to self(自赋值): class Widget { ... }; Widget w; ... w = w; // assignment to self 这看起来很愚蠢,但它是合法的,所以应该确信客户会这样做。另外,assignment(...

janeqi1987的专栏 386

vue3+vite:引入tailwindcss不生效的原因,详细解决Tailwind配置没问题但就是不生效的情况,配置正确无报错但样式没有生效(包括tailwindcss在Vue2使用不生效解决方案)

vue3 tailwind使用教程以及tailwind不生效的问题,vue3+vite引入tailwindcss不生效的原因,tailwindcss配之后不生效怎么办,如何解决tailwinscss不生效的原因,Vue3项目使用tailwindcss在Vue2使用,不生效解决,如何解决使用 Tailwind CSS 后出现的样式不生效的问题?tailwindcss配之后不生效 tailwindcss不生效,为什么我的 Tailwind css 类没生效,解决使用 Tailwind 时样式不生效的问题,配置没

✅ 精选推荐文章 4128

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

自行管理资源时,可能在停止使用资源之前意外释放了它。 class Data{...}; class Widget{ ... private: Data* pd; }; Widget& Widget::operator=(const Widget& rw) { delete pd; pd = new Data(*rw.pd); return *this; } operat...

lzhtcsw2016的博客 214

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

C++重载 = 时处理self-assignment

使用赋值运算符时有时会出现自我赋值的情况。当对于某个类重载=并进行自我赋值时,会出现一些问题。

bairimeng16的博客 527

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Effective C++ 11. Handle assignment to self in operator=

Handle assignment to self in operator=

ruihuank的博客 522

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

【条款11】Handle assignment to self in operator=

在重载“operator=”时,若类中涉及到了资源的动态分配,则一定要考虑自我赋值的情况,否则容易造成内存泄露。 下面是一个不安全的示例: class A{ public: A& operator=(const A& a) { delete data; size = a.size; data = new int[size]; for(int i=0; i<size; i++

traceorigin的专栏 619

cppcheck java_Cppcheck 1.54 C/C++静态代码分析工具

64-bit portabilityCheck if there is 64-bit portability issues:assign address to/from int/longAuto VariablesA pointer to a variable is only valid as long as the variable is in scope. Check:returning a ...

weixin_34805229的博客 386

11.Handle assignment to self in operator =

在operator = 中处理“自我赋值” 请记住: 确保当对象自我赋值时operator = 有良好行为。其中技术包括比较“来元对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap。 确保任何函数如果操作一个以上的对象,而其中多个对象时同一个对象时, 其行为依然正确。 ...

xupeng1644的博客 1551

c++ warning: ‘operator=‘ should check for assignment to self to avoid problems with dynamic memory

warning: 'operator=' should check for assignment to self to avoid problems with dynamic memory. [operatorEqToSelf]

xiaozhi 464

Effective C++条款11:在operator=中处理“自我赋值”(Handle assignment to self in operator=)

Effective C++条款11:在operator=中处理“自我赋值”(Handle assignment to self in operator=)

CltCj的博客 763

20230103编译ToyBrick的TB-RK3588X的Andorid12的LOG10

20230103编译ToyBrick的TB-RK3588X的Andorid12的LOG10。编译环境:Ubuntu18.04.6 64位。

南岭笑笑生之家 1753

Cppcheck 1.54 C/C++静态代码分析工具

Cppcheck是一个C/C++代码分析工具,只检测那些编译器通常无法检测到的bug类型。   官方上建议让编译器提供尽量多的警告提示: 1.使用Visual C++的话,应使用警告等级4 2.使用GCC的话,参看Warning options - using GCC   官方地址:http://cppcheck.sourceforge.net/ 当前版本:1.54 for wind

无幻 3万+

读书笔记 ---- 在operator=中处理"自我赋值"

最近在读《Effective C++》,对里面的思想和代码深有感触,因此在此做点记录并加以自己的理解,方便以后查看。 本文内容来自条款11:在operator=中处理"自我赋值"(Handle assignment to self in operator=.)。

enchanted的博客 505

《C++primer》第4版 和《C++编程思想》 读书笔记

/******************************** C++笔记 自己记录的觉得关键 容易忘得东西 内容杂乱没有次序不适合学习 参考内容来自《C++primer》第4版 《C++编程思想》第二版 第一卷第二卷 仅供参考 !!!!!!!!!!有错误的地方见谅可能抄错了!!!!!!!!!!!!! QQ710055071 十二年/DZF ********************

记录学习过程 2381
上一篇: [翻译] Effective C++, 3rd Edition, Item 10: 让 assignment operators(赋值运算符)返回一个 reference to *this(引向 *this 的引用)
下一篇: [翻译] Effective C++, 3rd Edition, Item 12: 拷贝一个 object(对象)的所有 parts(构件)
fatalerror99
博客等级 码龄24年 166粉丝 20原创
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值