`

Effective c++学习笔记——条款11:在operateor=中自我赋值

 
阅读更多
Handle assignment to self in operator=
本条款的核心是关于c++对象的自我赋值,既然说是自我赋值,那么就会产生一些你意想不到的问题。首先看一下很有意思的“自我赋值”,简单例子
// self_opera.cpp : 定义控制台应用程序的入口点。
//2011/9/11-by wallwind-in revenco
#include "stdafx.h"
#include<iostream>
using namespace std;

class myClass { };

int _tmain(int argc, _TCHAR* argv[]) {
myClass my;
my = my;
system("pause");
return 0;
}

上段程序是可以通过的。可能有时候自我赋值是不能一眼就看出来的。比如以下程序语句:
a[i] = a[j]; //潜在的自我赋值
当i=j的时候,这便是个自我赋值
又如
*px = *py; //潜在的自我赋值
如果*px和*py恰好指向同一个东西,这也是自我赋值;
这些并不明显的自我赋值,是“别名”带来的结果:所谓“别名”就是“有一个以上的方法指称(指涉)某对象”。一般而言如果某段代码操作pointers或references而它们被用来“指向多个相同类型的对象”,就需要考虑这些对象是否为同一个。实际上两个对象只要来自同一个继承体系,它们甚至不需要声明为相同类型就可能造成“别名”,因为一个base class的reference或pointer可以指向一个derived class对象:
就如以下代码:
// self_opera.cpp : 定义控制台应用程序的入口点。
//2011/9/11-by wallwind-in revenco
#include "stdafx.h"
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class Base{ };
class Derived:public Base {};
void dosomething(const Base &rb, Derived& pb )
{
cout<<&rb<<endl;
cout<<&pb<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
const Base rb;
Derived pb;
dosomething(rb,pb);
return 0;
}
输出结果为:
其实自我赋值的情况是很容易出现问题的。
下面给大家举一个比较简单实用的,类似于书中的例子,myclass内部维护char指针类型,并指向一块内存空间,在进行operator操作时,首先释放当前myclass类型buffer所指向的空间,并将buffer指向赋值右边同一空间。如果是指向同一个myclass类型呢?buffer所指向空间已经被释放,调用ToString方法时访问了已释放的空间,其结果未有定义。
// self_opera.cpp : 定义控制台应用程序的入口点。
//2011/9/11-by wallwind-in revenco
#include "stdafx.h"
#include <stdlib.h>
#include <string.h>
class myclass{
public:
myclass() {
buffer = new char[255];
memset(buffer, 65, sizeof(char) * 255);
}
~myclass() {
delete[] buffer;
}
char* ToString() const { return buffer; }
myclass& operator=(const myclass& rhs) {
delete[] buffer;
buffer = rhs.buffer;
return *this;
}
private:
char *buffer;
};
int _tmain(int argc, _TCHAR* argv[]) {
myclass str1;
printf("%sn", str1.ToString());
str1 = str1;
printf("%sn", str1.ToString());
system("pause");
return 0;
}
欲阻止这种错误,传统做法是由operator=最前面的一个“证同测试”达到“自我赋值”的检验目的:
如下代码
myclass& operator=(const String& rhs) {
if (rhs == * this)////////////////////////////此处要重写“==”
return *this;
delete[] buffer;
buffer = rhs.buffer;
return *this;
}
这样做行得通。稍早我曾经提过,前一版operator=不仅不具备“自我赋值安全性”,也不具备“异常安全性”,这个新版本仍然存在异常方面的麻烦。比如书中的例子给出这样表述,如果, rhs.buffer;
是一块空地址,或者异常地址,依然会出现以上情况。那么就有了另一种技术保证异常的自我赋值了。
在operator=函数内手工排列语句(确保代码不但“异常安全”而且“自我赋值安全”)的一个替代方案是,使用所谓的copy and swap技术。这个技术和“异常安全性”有密切关系,所以由条款29详细说明。然而由于它是一个常见而够好的operator=撰写办法,所以值得看看其实现手法像什么样子:
// self_opera.cpp : 定义控制台应用程序的入口点。
//2011/9/11-by wallwind-in revenco
#include "stdafx.h"
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class myclass {
public:
myclass() {
_buffer = new char[255];
memset(_buffer, 65, sizeof(char) * 255);
}
myclass(const myclass& rhs) {
_buffer = new char[255];
memcpy(_buffer, rhs._buffer, sizeof(char) * 255);
}
myclass& operator=(const myclass& rhs) {
myclass temp(rhs);
swap(temp);
return *this;
}
char* ToString() const { return _buffer; }
private:
void swap(const myclass& rhs) {
delete[] _buffer;
_buffer = rhs._buffer;
}
char *_buffer;
};
int _tmain(int argc, _TCHAR* argv[]) {
myclass str1;
printf("%sn", str1.ToString());
str1 = str1;
printf("%sn", str1.ToString());
system("pause");
return 0;
}
我个人比较忧虑这个做法,我认为它为了伶俐巧妙的修补而牺牲了清晰性。然而将“copy 动作”从函数本体内移至“函数参数构造阶段”却可令编译器有时生成更高效的代码。

请记住:

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

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

分享到:
评论

相关推荐

    C++学习书籍——effective c++ 3rd(中英文)

    C++学习书籍——effective c++ 3rd(中英文)

    Effective C++

    Effective C++(编程的50个细节)着重讲解了编写C++程序应该注意的50个细节问题,书中的每一条准则描述了一个编写出更好的C++的方式,每一个条款的背后都有具体范例支持,书中讲的都是C++的编程技巧和注意事项,很多都...

    Effective C++ 中文版

    条款11:在Operator=中处理“自我赋值” 条款12:复制对象时勿忘其每一个成分 3.资源管理 条款13:以对象管理资源 条款14:在资源管理类中小心Coping行为 条款15:在资源管理类中提供对原始资源的访问 条款16...

    Effective.C++.中文第二版.50条款doc文档.chm

    条款11: 为需要动态分配内存的类声明一个拷贝构造函数和一个赋值操作符 条款12: 尽量使用初始化而不要在构造函数里赋值 条款13: 初始化列表中成员列出的顺序和它们在类中声明的顺序相同 条款14: 确定基类有虚析构...

    Effective C++学习笔记

    Effective C++学习笔记 没有时间看Effictive C++书的人可以看看这个笔记 总结出了重点啊 适合有c++基础的人阅读

    effective c++读书笔记

    effective c++读书笔记和总结 effective c++读书笔记和总结

    Effective Modern C++学习笔记

    《Effective Modern C++:改善C++11和C++14的42个具体做法(影印版)(英文版)》中包括以下主题:剖析花括号初始化、noexcept规范、完美转发、智能指针make函数的优缺点;讲解std∷move,std∷forward,rvalue引用和全局...

    Effective C++学习笔记.doc

    自己整理的侯捷版本&lt;&lt;Effective C++ 改善程序与设计的55个具体做法&gt;&gt; 学习笔记.

    Effective Modern C++:改善C++11和C++14的42个具体做法(中文版 + 英文版)

    《Effective Modern C++:改善C++11和C++14的42个具体做法(影印版)(英文版)》中包括以下主题:剖析花括号初始化、noexcept规范、完美转发、智能指针make函数的优缺点;讲解std∷move,std∷forward,rvalue引用和...

    C++: Effective Modern C++ (C++ 11, C++ 14)

    C++: Effective Modern C++ (C++ 11, C++ 14) (guide,C Programming, HTML, Javascript, Programming,all,internet, Coding, CSS, Java, PHP Vol 1) By 作者: Paul Laurence ISBN-10 书号: 1547133244 ISBN-13 书号:...

    effective C++读书笔记

    effective C++读书笔记,effective C++读书笔记,effective C++读书笔记

    Effective C++(第三版)

    条款11:在operator= 中处理“自我赋值” handle assignment to self in operator=. 条款12:复制对象时勿忘其每一个成分 copy all parts of an object. 3. 资源管理 resource management 条款13:以对象管理资源 ...

    More Effective C++

    条款11:禁止异常(exceptions)流出 destructors 之外 058 Prevent exceptions from leaving destructors 条款12:了解「掷出一个 exception」与「传递一个参数」 或「调用一个虚函数」之间的差异 061 Understand ...

    effective C++ 读书笔记

    effective C++ 很好 很不错 最好的C++进阶资料

    effective C++ 读书笔记 PPT

    摘录了《Effective C++》 (Scott Meyers 著)中有参考价值的编写代码建议,方面阅读

    Effective C++跟more Effective c++

    Effective C++跟more Effective c++

    Effective C++ 超值合集(中英文)

    Effective C++ 超值合集,内容如下: (英文版)Effective C++ & More Effective C++ Effective C++中文第三版 More_Effective_C++(中文版).侯捷 More_Effective_Cpp 英文版

    Effective C++中文版第三版(带完整目录)(2-1)

    有人说c++程序员可以分成两类,读过effective c++的和没读过的。世界顶级c++大师scott meyers成名之作的第三版的确当得起这样的评价。当您读过这本书之后,就获得了迅速提升自己c++功力的一个契机。.(2-1)  在...

    effective c++和more effective c++

    电子版的effective c++ 和more effective c++ c++四书五经中介绍的经典书籍

Global site tag (gtag.js) - Google Analytics