Copy-and-swap详解安全自我赋值

Copy-and-swap详解安全自我赋值

  • 什么时候需要copy-swap?

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

  • 原则是什么?

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

  • 工作原理?

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

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

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

例子:

#include  // std::copy
#include  // std::size_t

class dumb_array
{
public:
	// (default) constructor
	dumb_array(std::size_t size = 0) :mSize(size),mArray(mSize ? new int[mSize]() : 0)
	{}
	// copy-constructor
	dumb_array(const dumb_array& other) :mSize(other.mSize),mArray(mSize?new                 int[mSize]:0)
	{
		// note that this is non-throwing, because of the data
		// types being used; more attention to detail with regards
		// to exceptions must be given in a more general case, however
		std::copy(other.mArray, other.mArray + mSize, mArray);
	}

	// destructor
	~dumb_array()
	{
		delete[] mArray;
	}

private:
	std::size_t mSize;
	int* mArray;
};

动态数组管理,dumb_array类对int*进行管理,已经定义了拷贝构造函数和析构函数,下面实现赋值函数。

  • 方法1

不具备异常安全,只具备自我赋值安全性

// the hard part
dumb_array& operator=(const dumb_array& other)
{
	if (this != &other) // (1) 避免自我赋值 造成重复分配内存和释放内存
	{
		// get rid of the old data...
		delete[] mArray; // (2)
		mArray = 0; // (2) *(see footnote for rationale)

		// ...and put in the new
		mSize = other.mSize; // (3)
		mArray = mSize ? new int[mSize] : 0; // (3) new分配失败的情况mArray指向的就是一块已经释放的内存
		std::copy(other.mArray, other.mArray + mSize, mArray); // (3) 
	}

	return *this;
}
  • 方法2

如果new出现异常,指针的值没变

这样就会导致代码膨胀,于是导致了另一个问题:代码冗余

dumb_array& operator=(const dumb_array& other)
{
    if (this != &pOther) // (1)
    {
        // get the new data ready before we replace the old
        std::size_t newSize = other.mSize;
        int* newArray = newSize ? new int[newSize]() : 0; // (3)
        std::copy(other.mArray, other.mArray + newSize, newArray); // (3)
 
        // replace the old data (all are non-throwing)
        delete [] mArray;
        mSize = newSize;
        mArray = newArray;
    }
 
    return *this;
} 
  • 方法3

copy and swap

#include  // std::copy
#include  // std::size_t
#include 
using namespace std;

class dumb_array
{
public:
	// (default) constructor
	dumb_array(std::size_t size = 0) :mSize(size), mArray(mSize ? new int[mSize]() : 0)
	{}
	// copy-constructor
	dumb_array(const dumb_array& other) :mSize(other.mSize), mArray(mSize ? new                 int[mSize] : 0)
	{
		// note that this is non-throwing, because of the data
		// types being used; more attention to detail with regards
		// to exceptions must be given in a more general case, however
		std::copy(other.mArray, other.mArray + mSize, mArray);
	}

	// destructor
	~dumb_array()
	{
		delete[] mArray;
	}

	// ...
	//自定义一个swap友元类
	friend void swap(dumb_array& first, dumb_array& second); // nothrow
	//注意这里赋值是按照值赋值的,实参传递给形参的时候调用拷贝构造函数,初始化形参
	//形参值在内部被修改了,但是不影响实参的使用
	dumb_array& operator=(dumb_array other) // (1)
	{
		swap(*this, other); // (2)
		return *this;
	}
	// ...
	void print()
	{
		cout << (long)mArray << endl;
	}

private:
	std::size_t mSize;
	int* mArray;
};

//自定义一个swap友元类
void swap(dumb_array& first, dumb_array& second) // nothrow
{
	// enable ADL (not necessary in our case, but good practice)
	using std::swap;

	// by swapping the members of two classes,
	// the two classes are effectively swapped
	swap(first.mSize, second.mSize);
	swap(first.mArray, second.mArray);
}

int main()
{
	dumb_array array1(5);
	array1.print();
	dumb_array array2(7);
	array2.print();
	array1 = array2;
	array1.print();		//交换完了 地址变化了,说明新分配了地址
	array2.print();		//交换玩 地址没有改变
	return 0;
}
  • 方法4 C++11
#include  // std::copy
#include  // std::size_t
#include 
using namespace std;

class dumb_array
{
public:
	// (default) constructor
	dumb_array(std::size_t size = 0) :mSize(size), mArray(mSize ? new int[mSize]() : 0)
	{}
	// copy-constructor
	dumb_array(const dumb_array& other) :mSize(other.mSize), mArray(mSize ? new                 int[mSize] : 0)
	{
		// note that this is non-throwing, because of the data
		// types being used; more attention to detail with regards
		// to exceptions must be given in a more general case, however
		std::copy(other.mArray, other.mArray + mSize, mArray);
	}

	// destructor
	~dumb_array()
	{
		delete[] mArray;
	}

	// ...
	//自定义一个swap友元类
	friend void swap(dumb_array& first, dumb_array& second); // nothrow
	//注意这里赋值是按照值赋值的,实参传递给形参的时候调用拷贝构造函数,初始化形参
	//形参值在内部被修改了
	//dumb_array& operator=(dumb_array other) // (1)
	//{
	//	swap(*this, other); // (2)
	//	return *this;
	//}
	// ...
	void print()
	{
		cout << (long)mArray << endl;
	}

	// move ctor 移动构造函数
	dumb_array(dumb_array&& other)
	{
		std::cout << "move ctor" << std::endl;
		mSize = other.mSize;
		mArray = other.mArray;
		if (other.mArray)
		{
			other.mArray = nullptr;
		}	
	}

	// move assignment移动赋值函数
	dumb_array &operator=(dumb_array &&rhs)
	{
		std::cout << "move assignment" << std::endl;
		//dumb_array tmp(rhs);
		swap(*this, rhs);
		return *this;
	}


private:
	std::size_t mSize;
	int* mArray;
};

//自定义一个swap友元类
void swap(dumb_array& first, dumb_array& second) // nothrow
{
	// enable ADL (not necessary in our case, but good practice)
	using std::swap;

	// by swapping the members of two classes,
	// the two classes are effectively swapped
	swap(first.mSize, second.mSize);
	swap(first.mArray, second.mArray);
}

dumb_array getRightValue()
{
	dumb_array array3(7);
	return array3;//这里调用移动构造函数 调用完array3的值会发生变化
}

int main()
{
	dumb_array array1(5);
	array1.print();
	dumb_array array2(7);
	array2.print();
	//array1 = getRightValue(); //
	array1 = std::move(array2); //这里调用的是移动赋值函数 调用完array2值不变
	array1.print();		//交换完了 地址变化了,说明新分配了地址
	array2.print();		//交换玩 地址没有改变



	return 0;
}

dumb_array& operator=(dumb_array other) 与**dumb_array &operator=(dumb_array &&rhs)**不能同时定义否则会编译出错。

你可能感兴趣的:(C++学习记录,C++,copy-and-swap)