有指针或者多维数组时,如何写 拷贝构造函数,移动构造函数,拷贝赋值运算符,移动赋值运算符

当成员变量里面有指针或者多维数组时,如何写 拷贝构造函数,移动构造函数,拷贝赋值运算符,移动赋值运算符

头文件

#pragma once
#include
using namespace std;
const int num = 5;
/*
重写C++默认函数
*/
class DefaultRewrite
{
public:
	DefaultRewrite();
	// 拷贝构造函数
	DefaultRewrite(const DefaultRewrite& other);
	// 手动定义拷贝赋值运算符
	DefaultRewrite& operator=(const DefaultRewrite& other);
	// 移动构造函数
	DefaultRewrite(DefaultRewrite&& other);
	// 自定义移动赋值运算符
	DefaultRewrite& operator=(DefaultRewrite&& other);
	void initBuffer(char* copy,int size);
	void printDefaultRewrite();
private:

	char* m_pBuf;
	int cal[num][num];
	int m_bufSize;
};


实现

#include "DefaultRewrite.h"


DefaultRewrite::DefaultRewrite():
	m_pBuf(nullptr), cal{}, m_bufSize(num)
{
	cout << "默认构造函数" << endl;
}

DefaultRewrite::~DefaultRewrite()
{
	if (m_pBuf  )
	{
		delete[] m_pBuf;
	}
	m_pBuf = nullptr;
	cout << "析构函数" << endl;
}
void DefaultRewrite::initBuffer(char* copy, int size)
{
	int minsize = min(size, m_bufSize);
	if (m_pBuf == nullptr)
	{
		m_pBuf = new char[m_bufSize];
	}
	//std::copy(copy, copy + minsize, m_pBuf);
	memcpy(m_pBuf, copy, m_bufSize);
	for (int i = 0; i < num; i++) 
	{
		for (int j = 0; j < num; j++) 
		{
			cal[i][j] = i+j;
		}
	}
}

// 拷贝构造函数
DefaultRewrite::DefaultRewrite(const DefaultRewrite& other)
	: m_pBuf(other.m_pBuf) , m_bufSize(other.m_bufSize)
{
	cout << "拷贝构造函数" << endl;
	std::copy(&other.cal[0][0], &other.cal[0][0] + num * num, &cal[0][0]);
	if (other.m_pBuf)
	{
		m_pBuf = new char[m_bufSize];

		//std::copy(other.m_pBuf, other.m_pBuf + m_bufSize, m_pBuf);
		memcpy(m_pBuf, other.m_pBuf, m_bufSize);
	}
	else
	{
		m_pBuf = nullptr;
	}

}

// 拷贝赋值运算符
DefaultRewrite& DefaultRewrite::operator=(const DefaultRewrite& other) {
	cout << "拷贝赋值运算符" << endl;
	
	if (this == &other) return *this;

	if (m_pBuf)
	{
		delete[] m_pBuf;
	}
	// 深拷贝
	m_pBuf = new char[m_bufSize];
	std::copy(&other.cal[0][0], &other.cal[0][0] + num * num, &cal[0][0]);
	//std::copy(other.m_pBuf, other.m_pBuf + m_bufSize, m_pBuf);
	memcpy(m_pBuf, other.m_pBuf, m_bufSize);
	return *this;
}

// 移动构造函数
DefaultRewrite::DefaultRewrite(DefaultRewrite&& other) 
	: m_pBuf(other.m_pBuf), m_bufSize(other.m_bufSize)
{
	
	cout << "移动构造函数" << endl;
	other.m_pBuf = nullptr;
	std::copy(&other.cal[0][0], &other.cal[0][0] + num * num, &cal[0][0]);
}

// 移动赋值运算符
DefaultRewrite& DefaultRewrite::operator=(DefaultRewrite&& other) 
{
	
	cout << "移动赋值运算符" << endl;
	if (this == &other) return *this;

	if (m_pBuf)
	{
		delete[] m_pBuf;
	}

	m_pBuf = other.m_pBuf;
	m_bufSize = other.m_bufSize;
	std::copy(&other.cal[0][0], &other.cal[0][0] + num * num, &cal[0][0]);

	other.m_pBuf = nullptr;

	return *this;
}

void DefaultRewrite::printDefaultRewrite()
{
	if(m_pBuf)
	cout << m_pBuf << endl;
	for (int i = 0; i < num; i++)
	{
		for (int j = 0; j < num; j++)
		{
			cout << cal[i][j] << "  ";
		}
		cout << endl;
	}
}

main

int main()
{	DefaultRewrite obj1; // 默认构造函数
	cout << "obj1" << endl;
	obj1.printDefaultRewrite();

	char str[] = "hi";
	obj1.initBuffer(str,3);
	cout << "obj1 initBuffer" << endl;
	obj1.printDefaultRewrite();

	DefaultRewrite obj2 = obj1; // 拷贝构造函数
	cout << "obj2" << endl;
	obj2.printDefaultRewrite();
	

	DefaultRewrite obj3;
	obj3 = obj1; // 拷贝赋值运算符
	cout << "obj3" << endl;
	obj3.printDefaultRewrite();

	DefaultRewrite obj4(std::move(obj2)); // 移动构造函数
	cout << "obj4" << endl;
	obj4.printDefaultRewrite();

	DefaultRewrite obj5;
	obj5 = std::move(obj1); // 移动赋值运算符
	cout << "obj5" << endl;
	obj5.printDefaultRewrite();


	cout << "obj1" << endl;
	obj1.printDefaultRewrite();

	cout << "obj2" << endl;
	obj2.printDefaultRewrite();
}

运行结果
有指针或者多维数组时,如何写 拷贝构造函数,移动构造函数,拷贝赋值运算符,移动赋值运算符_第1张图片

注意事项

move

在代码中调用obj3 = std::move(obj1)后,obj1之所以还可以继续被使用,主要原因是:

移动语义的转移是通过“窃取”资源的方式实现的。

  1. std::move(obj1)只是将obj1转换为一个右值,本身不会做实际的资源转移。

  2. 随后调用DefaultRewrite的移动赋值运算符实现资源的转移。

  3. 但移动赋值运算符只将obj1的m_pBuf指针转移给了obj3。

  4. obj1的其他成员如m_bufSize等并没有被修改。

  5. 所以obj1的成员中只有m_pBuf被置为空指针,其他成员依然保持有效状态。

  6. 这就是移动语义的“destructive move” 特性,移动后源对象可处于任意状态。

  7. 如果obj1的其他成员没有再次使用m_pBuf,那么obj1仍可继续正常使用。

  8. 但需要注意,obj1的m_pBuf已经失效,不能再被访问。

总结来说,移动语义下源对象可被部分重用,这 depends on 的是资源的转移方式。如果只转移部分资源,源对象可能依然可以使用剩余的资源。

数组初始化与赋值

cal=other.cal;为什么会报错
数组名是一个地址常量,其值和第一个元素的地址值相同,不可修改。赋值号左边必须是一个变量
https://blog.csdn.net/huang1600301017/article/details/88562653

深拷贝

数组和指针在这四个函数里面都需要手动深拷贝

在移动构造函数中,对指针成员m_pBuf我们直接进行了浅拷贝:

m_pBuf = other.m_pBuf; 

移动语义

没有进行深拷贝, 是可以的。主要原因有:

  1. 移动构造的目的是资源的转移,而不是共享。

  2. 我们期望移动操作后,源对象不再使用这部分资源。

  3. 直接使两个对象的指针指向同一区域,即实现了资源的转移。

  4. 如果我们深拷贝,就会造成资源冗余,降低效率。

  5. 移动后源对象的指针会被置空,不会再访问这个区域。

  6. 所以这种“浅拷贝”方式实现了零开销的资源转移。

  7. 这也是移动语义的设计目的,比深拷贝更高效。

当然,如果指针指向的内容还会被源对象使用,我们就不能这样直接转移指针。

但在移动语义下,通常假设源对象资源不再被使用,所以直接指针赋值即可优化。

这是移动构造可以进行资源“窃取”的原理所在。

你可能感兴趣的:(C++,c++)