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

本文讨论了在重载'operator='时,类中涉及动态资源分配时如何处理自我赋值和异常安全性的挑战。通过实例分析了错误实现可能导致的内存泄露,并提出了改进方法,包括增加验证测试、确保异常安全性和使用复制交换技术来解决这些问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在重载“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++)
			data[i] = a.size[i];
		return *this;
	}
private:
	int *data;
	int size;
};
当出现自我赋值的情况时,data被释放掉了,而在循环中任然会试图访问之,显然这是非法的。

改正方法是增加“证同测试”

A& operator=(const A& a)
{
	if(this == &a) return *this;
	
	delete data;
	size = a.size;
	data = new int[size];
	for(int i=0; i<size; i++)
		data[i] = a.size[i];
	return *this;
}
但是该版本可能具有“异常安全性”,因为如果分配时内存不足,会使得data指向一块被删除的内存,这样的指针式有害的。但你无法删除它,有无法读取他,很无奈。

一种解决方案是:不管“自我赋值”问题,只关注“异常安全”问题。示例如下:

A& operator=(const A& a)
{
	int *pOrigin = data;	
	size = a.size;
	data = new int[size];
	for(int i=0; i<size; i++)
		data[i] = a.size[i];	
	delete pOrigin;
	
	return *this;
}
这样,即使new操作发生异常,data仍然保持原状,指向原来的内存。

另外一种,即确保“异常安全”又确保“自我赋值安全”的一个替代方案是:copy and swap 技术。

A& operator=(const A& a)
{
	A temp(a);	//为a做了一个副本
	swap(temp);	//将*this与a的副本交换。
	return *this;
}

还有一种巧妙的方法是,利用copy assignment的实参的值传递,因为值传递会构建一个副本

A& operator=(A a)	//a是实参的一个副本
{
	swap(temp);	//将*this与a的副本交换。
	return *this;
}







### 关于编程中的 `operator=` 使用或错误 `operator=` 是 C++ 中的一个特殊成员函数,用于实现对象赋值操作。它允许开发者定义如何将一个对象的内容复制到另一个对象中。如果未显式定义该运算符,则编译器会自动生成默认版本[^3]。 #### 默认行为 当类没有提供自己的 `operator=` 实现时,C++ 编译器会生成一个默认的拷贝赋值运算符。这个默认版本逐成员地执行浅拷贝(shallow copy)。这意味着对于指针类型的成员变量,仅复制其地址而非实际数据内容[^4]。 ```cpp class MyClass { public: int* ptr; // Default constructor MyClass() : ptr(new int(0)) {} ~MyClass() { delete ptr; } }; // Example of shallow copying leading to undefined behavior. void testDefaultAssignmentOperator() { MyClass obj1; *obj1.ptr = 10; MyClass obj2; obj2 = obj1; // Uses default assignment operator std::cout << "*obj2.ptr: " << *obj2.ptr << "\n"; // Outputs 10 // Both objects now point to same memory location after destruction leads to double-free error } ``` 上述代码展示了由于使用默认赋值运算符而导致的双重释放问题[^5]。 #### 自定义 `operator=` 为了避免潜在的风险,在某些情况下需要重载 `operator=` 来实施深拷贝(deep copy),即创建新内存并复制原始数据至其中: ```cpp class MyClassWithDeepCopy { private: int* data; public: explicit MyClassWithDeepCopy(int value) : data(new int(value)) {} ~MyClassWithDeepCopy() { delete data; } // Copy Assignment Operator Overload using copy-and-swap idiom MyClassWithDeepCopy& operator=(const MyClassWithDeepCopy& other) { auto temp = new int(*other.data); swap(data, temp); delete temp; return *this; } }; ``` 此方法遵循了 **copy-and-swap** 范式来确保强异常安全性以及自我赋值的安全处理[^6]。 #### 常见错误及其解决方案 以下是几个常见的关于 `operator=` 的误用场景及对应的修正措施: 1. **忽略返回类型** 如果忘记让 `operator=` 返回当前实例 (`return *this`) ,则链式调用将会失效。 2. **缺乏对自赋值的支持** 当目标和源是同一个对象时可能发生意外情况;因此应始终考虑这种情况下的正确性保障。 3. **资源泄漏风险** 对动态分配的数据结构进行管理不当容易引发内存泄露等问题,故需谨慎对待每一步骤以防止此类隐患发生。 #### 并发环境下的注意事项 在多线程环境中应用 `operator=` 可能面临额外挑战。例如,两个不同线程可能同时尝试修改同一共享状态的对象副本。此时应当引入同步机制保护关键区域免受竞争条件影响[^7]: ```cpp std::mutex mtx; class ThreadSafeObject { protected: mutable std::mutex m_mutex; int value_; public: ThreadSafeObject(const ThreadSafeObject &rhs) noexcept(false){ std::lock_guard<std::mutex> lock(rhs.m_mutex); this->value_ = rhs.value_; }; ThreadSafeObject& operator=(ThreadSafeObject const& other) { if(this != &other){ std::unique_lock<std::mutex> lhsLock(m_mutex,std::defer_lock), rhsLock(other.m_mutex,std::defer_lock); std::lock(lhsLock,rhsLock); // Avoid deadlock value_=other.value_; } return *this; }; }; ``` 通过以上方式可以有效减少因并发访问带来的不确定性因素干扰程序正常运行流程的可能性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值