C++-C++ operator=为什么要判断自我赋值

发布于 2016-10-17 02:52:43 字数 73 浏览 1252 评论 5

在C++中,编写操作符operator=的时候常常要判断是否是自我赋值。我想问的是,为什么要做这个判断?不判断程序不可以正常运行吗?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

清晨说ぺ晚安 2017-10-03 12:52:53

自己赋值自己是无意义操作,如果类很大,自己赋值自己太浪费cpu了

甜柠檬 2017-08-16 14:52:30

《Effective C++》 条款十一:在operator=中处理assignment to self 中有详细的说明。

清晨说ぺ晚安 2017-06-26 15:56:48

原因一:防止自我赋值导致的资源过早释放,从而避免悬挂指针

泛泛之交 2017-02-25 03:51:32

赋值操作只要你写的正确,程序可以正常运行。但是自我赋值完全没有意义,是对计算机的资源的一种浪费,何必再释放一次资源有分配一次资源呢。而且如果你对赋值写的不够健壮会出现问题。
举个例子

class A {
public:
A& operator= (const A& rhs) {
//if(this == &rhs) return *this; 如果是自我赋值就没必要执行下面的操作了。
delete ch; //如果是自我赋值 这里delete ch 也把rhs.ch的内存空间释放了。这会出现问题。这是不好的写法。
ch = new char[strlen(rhs.ch)+1];
strcpy(ch,rhs.ch);
return *this;
}
//正确 健壮的写法
A& operator= (const A& rhs) {
//if(this == &rhs) return *this;
char* temp = new char[strlen(rhs.ch)+1]; //这里如果申请资源失败了,就没必要继续往下执行了。
strcpy(temp,rhs.ch);
delete ch;
ch = temp;
return *this;
}
private:
char* ch;
};

泛泛之交 2017-01-22 17:49:20

我们在写operator= 函数实现时,要注意一个问题:要考虑对象自我赋值的情况,因为客户完全可以写
下如下代码:

Widget w;
...
w=w;

这样写完全合法,那么我们在写Widget::operator=(xx)的实现时,一定要考虑到这个问题,否则一些想象不到的问题就来"拜访"你了,呵呵,比如,现在有一个类Widget:

class Bitmap{};
class Widget{
public:
...
Widget& operator=(const Widget& rhs){
delete hBitmap_;//hBitmap_可能为NULL,注意:delete 删除NULL指针是可以的,它会什么都不做.
hBitmap_ = new Bitmap(*rhs.hBitmap_);
return *this;
}
private:
Bitmap* hBitmap_;
};

这里的rhs与*this如果要是同一个对象,会出现什么问题?也就是说两个对象的hBitmap_是同一个图片
数据,当delete hBitmap_;被执行时,它们指向的同一个Bitmap资源被释放掉,紧接着执行下面一条语句就出
现了问题,因为新产生的hBitmap_是一个无效对象(它占用的资源对象已经被释放掉了).程序继续往下跑的时
候就会可能出现访问内存等问题.这个时候你注意到了这个问题,你开始修改代码:

  Widget& Widget::operator=(const Widget& rhs){
if(this == &rhs){ //增加了判断条件
return *this;
}
delete hBitmap_;
hBitmap_ = new Bitmap(*rhs.hBitmap_); // throw possibel exception?
return *this;
}

代码增加了逻辑判断语句,这样就解决了"自我赋值安全"问题,然而如果new Bitmap(*rhs.hBitmap_);这条语
句出现了异常,那么hBitmap_就是一个不可预料值,这样就很可怕,导致了"异常性安全"问题的出现.
往往让operator=获得了"异常安全性"却会自动获得"自我赋值安全"的回报,所以很多人把焦点放在了"异常安全性",
而对于"自我赋值安全性"就放任其不管,于是他们想出这样的方案:在赋值之前先不去删除以前的对象.这样就写出
如下代码:

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

这样的话,如果"new Bitmap(*rhs.hBitmap_);"抛出异常,左右操作对象都保持不变,保持了"异常安全性",而它也保持
了"自我赋值安全性",这行得通,不过你要是考虑到效率问题的话,有一种注重"异常安全性"的替代方案将是你的更好的
选择,它是通过"copy and swap"实现的(条款29将会做更详细的探讨):

  class Widget{
public:
Widget& operator=(const Widget& rhs){
Widget tempt(rhs); //make a source copy
swap(rhs);
return *this;
}
...
private:
void swap(const Widget& rhs); //交换两个Widget对象数据
...
};

由于改实现要将产生一个参数的副本所以说我们可以通过"pass by value"来传递参数,我们再次改写代码:

  class Widget{
public:
Widget& operator=(Wiget rhs){
swap(rhs);
return *this;
}
....
private:
void swap(const Widget& rhs); //交换两个Widget对象数据
...
};

这样的函数签名看起来虽然看起来让人不是那么舒服,虽然它为了巧妙的修改代码却牺牲了代码可读性,然而将
"copying动作"从函数本体移至"函数参数构造阶段"却可令编译器有时生成更高效代码.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文