关于operator=的自我赋值问题

今天看了一段C++Primer的代码:p506

//use-counted assignment operator;use is a pointer to a shared use count
Sales_item&
Saler_item::operator=(const Sales_item &rhs)
{
	++*rhs.use;
	decr_use();
	p = rhs.p;
	use = rhs.use;
	return *this;
}
其中
void decr_use()
{
	if(--*use==0)
	{
		delete p;
		delete use;
	}
}
问题是:上面是如何处理自我赋值问题的。我看了半天,不太理解。

后来才发现:它所谓的防止自我赋值的核心问题就是要防止自我赋值过程中,被赋值对象被提前删除,而造成程序出错。注释后结果:

//use-counted assignment operator;use is a pointer to a shared use count
Sales_item&
Saler_item::operator=(const Sales_item &rhs)
{
	++*rhs.use; //右操作数的使用计数加1,这样就可以避免左右操作数相同,造成左操作数提前析构
	//因为调用decr_use后,左操作数就至少是2了,减1后不为零,就不会出现delete操作
	decr_use(); //这个decr_use是左操作数调用的,左操作数的使用计数减1
	p = rhs.p;  //将右操作数的指针赋给左操作数
	use = rhs.use;
	return *this;//返回左操作数的引用
}
其中
void decr_use()
{
	if(--*use==0)
	{
		delete p;
		delete use;
	}
}

现在来看Meyers的Effective C++中的“自我赋值”处理   p54

看如下代码:

Widget&
Widget::operator=(const Widget&rhs)
{
	delete pb;   //停止使用当前的bitmap
	pb = new Bitmap(*rhs.pb);//使用rhs的bitmap副本
	return *this;
}

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

欲阻止这种错误,传统做法是借有operator=最前面的一个证同测试达到自我赋值的检验目的:

Widget&
Widget::operator=(const Widget&rhs)
{
	if (this==&rhs) return *this;//证同测试
	delete pb;   //停止使用当前的bitmap
	pb = new Bitmap(*rhs.pb);//使用rhs的bitmap副本
	return *this;
}
不过这一改进仍有一个麻烦,那就是当new Bitmap出现异常(不论是因为分配时内存不足或因为Bitmap的copy构造函数抛出异常),Widget最终会持有一个指针指向一块被删除的Bitmap。这样的指针是有害的。你无法安全删除它们,甚至无法安全读取它们。那么,我们要做的就是让operator=具有异常安全性,而且在operator=具有异常安全性后往往能够自动获得“自我赋值安全”的回报。修改如下:

Widget&
Widget::operator=(const Widget&rhs)
{
	Bitmap* pOrig = pb;
	pb = new Bitmap(*rhs.pb);//使用rhs的bitmap副本
	delete pOring;
	return *this;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用中提到,运算符重载函数“operator=”是一个赋值运算符的重载函数。该函数用于将一个对象的值赋给另一个对象,从而实现对象之间的赋值操作。在C++中,赋值运算符重载函数的名字是"operator=",它可以被定义为类的成员函数或全局函数。 如果赋值运算符重载函数被定义为类的成员函数,那么它将用于将一个对象的值赋给该类的另一个对象。例如,如果有一个person类,可以定义一个赋值运算符重载函数来实现对象之间的赋值操作,就像这样: ```cpp person& operator=(const person& ps) { if (this != &ps) { this->age = ps.age; } return *this; } ``` 在上面的例子中,赋值运算符重载函数使用了const引用参数,接受一个person对象作为参数,并将该对象的age值赋给当前对象的age成员变量。此外,还需要注意在函数体内部判断了两个对象是否是同一个对象,以避免自我赋值问题。 如果赋值运算符重载函数被定义为全局函数,那么它将用于将一个对象的值赋给不同类的对象。例如: ```cpp person operator=(const person& ps1, const person& ps2) { person temp; temp.age = ps1.age; return temp; } ``` 在上面的例子中,全局函数的参数包括两个person对象,它将ps1对象的age值赋给一个新的person对象temp,并将temp对象作为返回值返回。 总之,赋值运算符重载函数允许我们自定义对象之间的赋值操作,以便适应特定的需求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [C++编程语言中重载运算符(operator)介绍](https://blog.csdn.net/liitdar/article/details/80654324)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值