Copy and Swap技术-安全自我赋值

        关于C++的赋值运算符的重写,effective C++上已经有足够详细的描述,但是对于拷贝交换技术只是简单的提及,作者对此的看法是不提倡。我认为事实上拷贝交换技术还是非常有学习和应用的必要的,其关键在于,把一切编译器可以完成的工作完全交给编译器去做,而不是由我们去手工实现,从而避免了很多错误。

什么时候需要copy-swap?

        构造一个类去管理另外一个类时,需要遵循一个原则( The Rule of Three ),拷贝构造函数,赋值函数,析构函数,如果显示的实现其中一个,其他的都需要显示实现。

原则是什么?

copy-swap是解决方案,可以很好地协助赋值运算符实现两件事:避免代码重复,并提供强大的异常保证。

工作原理?

        从概念上讲,它通过使用拷贝构造函数的功能来创建数据的本地副本,然后使用交换功能获取复制的数据,将旧数据与新数据交换来工作。然后,临时副本将销毁,并随身携带旧数据。我们剩下的是新数据的副本。

为了使用copy-swap,我们需要三件事:

  1. 一个有效的拷贝构造函数
  2. 一个有效的析构函数
  3. 一个自定义的交换函数,不能用std::swap,因为该函数实现中调用了拷贝构造和复制函数,且交换函数不抛异常

示例

class Test
{
public:
	Test(int a, int b):m_nA(a),m_pB(new int(b))
	{
		cout << "构造:m_a:" << m_nA << " " << *m_pB << endl;
	}
	Test(const Test& obj)
	{
		this->m_nA = obj.m_nA;
		this->m_pB = new int(*obj.m_pB);		
	}

	Test& operator=(const Test& obj)
	{
		if (this != &obj)//避免自我赋值
		{
			delete m_pB;
			this->m_nA = obj.m_nA;
			this->m_pB = new int(*obj.m_pB);//可能失败
		}

		return *this;//返回*this的引用
	}

	~Test()
	{
		
		if (m_pB)
		{
			cout << "析构:m_a:" << m_nA << " " << *m_pB << endl;
			delete m_pB;
			m_pB = nullptr;
		}
	}
private:
	int m_nA;
	int* m_pB{nullptr};
};

        可以看到我们的代码几乎是对拷贝构造函数和析构函数的完全复制,此外,上述代码虽然完成了自赋值的验证,但并未保障异常安全。一旦new失败,原this对象的m_pB已经被删除,因此会引发异常。

下面给出另一种写法:

void swapEx(Test& obj)
	{
		using std::swap;
		swap(this->m_nA, obj.m_nA);
		swap(this->m_pB, obj.m_pB);
	}
	Test& operator=(const Test& obj)
	{
		if (this != &obj)//避免自我赋值
		{
			Test tmp(obj);
			swapEx(tmp);
		}

		return *this;
	}

        上述第一个方法事实上是致命的。在不考虑继承关系的复杂情况下,如果更改类A,添加数据成员,我们在修改其它构造/析构函数的同时,也必须修改赋值运算符。copy and swap技术则可以做到完全规避这一点,此外,所有调用工作由编译器自动完成,无需再做任何额外操作。
该技术的核心就是不再使用引用作为赋值运算符参数,形参将直接是对象,这样的写法将会使编译器自动调用拷贝构造函数,由于拷贝构造函数的调用,异常安全将在进入函数体之前被避免(若拷贝失败则什么都不会发生)。经过swap后的对象在离开函数体后会自动销毁,因此也就自动调用了析构函数。

参考链接:https://blog.csdn.net/hiwubihe/article/details/116667884
参考链接:https://blog.csdn.net/feifeiiong/article/details/77866579

你可能感兴趣的:(Effective,C++,c++,算法,开发语言)