C++ 深拷贝和浅拷贝

虽然我们知道了拷贝构造函数,但是大多数简单程序中都不需要特别编写拷贝构造函数,隐含的拷贝构造函数足以实现对象间数据元素的一一对应复制。但是隐含的拷贝构造函数并不总是适用的,因为它完成的只是浅拷贝。

1.浅拷贝

【例】对象的浅拷贝

cpp 复制代码
class Point
{
public:
	Point() :x(0), y(0)
	{
		cout << "调用默认构造函数" << endl;//数组元素的地址

	}

	Point(int x, int y) :x(x), y(y)
	{
		cout << "调用构造函数" << endl;
	}
	~Point()
	{
		cout << "调用析构函数" << endl;
	}
	int getX() 
	{
		return x;
	}
	int getY() 
	{
		return y;
	}
	void move(int newX, int newY)
	{
		x = newX;
		y = newY;
		cout << "(" << x << "," << y << ")" << endl;//对应数组元素的地址
	}
private:
	int x, y;
};

//动态数组类
class Arr
{
public:
	Arr(int size) :size(size)
	{
		points = new Point[size];

	}
	~Arr()
	{
		cout << "删除对象:" << endl;
		delete[]points;
	}
	//获得下标为index的数组元素
	Point& element(int index)
	{
		assert(index >= 0 && index < size);//如果数组下标越界,程序中止
		return points[index];
	}
private:
	Point* points;//指向动态数组的首地址
	int size;//数组大小
};

int main()
{
	int count;
	cout << "请输入点的个数:";
	cin >> count;
	Arr parr1(count);
	parr1.element(0).move(1, 2);
	parr1.element(1).move(2, 3);
	parr1.element(2).move(3, 4);


	Arr parr2(parr1);

	cout << "从parr1拷贝过来的点:" << endl;
	cout << "parr2的第一个元素:" << "(" << parr2.element(0).getX() << "," << parr2.element(0).getY() << ")" << endl;
	cout << "parr2的第二个元素:" << "(" << parr2.element(1).getX() << "," << parr2.element(1).getY() << ")" << endl;
	cout << "parr2的第三个元素:" << "(" << parr2.element(2).getX() << "," << parr2.element(2).getY() << ")" << endl;

	cout << "更新parr1的内容:" << endl;
	parr1.element(0).move(10, 20);
	parr1.element(1).move(20, 30);
	parr1.element(2).move(30, 40);

	cout << "更新parr1的内容之后parr2的内容:" << endl;
	cout << "parr2的第一个元素:" << "(" << parr2.element(0).getX() << "," << parr2.element(0).getY() << ")" << endl;
	cout << "parr2的第二个元素:" << "(" << parr2.element(1).getX() << "," << parr2.element(1).getY() << ")" << endl;
	cout << "parr2的第三个元素:" << "(" << parr2.element(2).getX() << "," << parr2.element(2).getY() << ")" << endl;

	return 0;

}

运行结果:

结果分析:

程序中parr2的内容是从parr1中拷贝过来的,它们的初始状态是一样的,但是当程序通过move函数更新parr1中的第一组点之后,parr2中的第二组点也被更新到了同样的位置。这就说明两组点之间存在着某种必然的联系,而这种联系并不是我们所期望的。

这里建立对象parr2时调用的是隐含的默认拷贝构造函数,实现对应数据项的直接拷贝。如下图所示:

当程序中更新parr1中的点时,也影响到了parr2。这种效果就是"浅拷贝"。

浅拷贝还有很大的弊病,在程序结束之前parr1和parr2的析构函数会被自动调用,在把理想的结果输出完成后会连续不停止地一直输出"调用析构函数",动态内存的分配空间会被释放。由于两个对象共用了一块内存空间,因此该空间被释放了两次,于是导致运行错误。解决这一问题的方法是编写拷贝构造函数,实现"深拷贝"。

1.浅拷贝

【例】对象的深拷贝

cpp 复制代码
class Point
{
public:
	Point() :x(0), y(0)
	{
		cout << "调用默认构造函数" << endl;//数组元素的地址

	}

	Point(int x, int y) :x(x), y(y)
	{
		cout << "调用构造函数" << endl;
	}
	~Point()
	{
		cout << "调用析构函数" << endl;
	}
	int getX()
	{
		return x;
	}
	int getY()
	{
		return y;
	}
	void move(int newX, int newY)
	{
		x = newX;
		y = newY;
		cout << "(" << x << "," << y << ")" << endl;//对应数组元素的地址
	}
private:
	int x, y;
};

//动态数组类
class Arr
{
public:
	Arr(const Arr& v);
	Arr(int size) :size(size)
	{
		points = new Point[size];

	}
	~Arr()
	{
		cout << "删除对象:" << endl;
		delete[]points;
	}
	//获得下标为index的数组元素
	Point& element(int index)
	{
		assert(index >= 0 && index < size);//如果数组下标越界,程序中止
		return points[index];
	}
private:
	Point* points;//指向动态数组的首地址
	int size;//数组大小
};

Arr::Arr(const Arr& v)
{
	size = v.size;
	points = new Point[size];
	for (int i = 0; i < size; i++)
	{
		points[i] = v.points[i];
	}
}
int main()
{
	int count;
	cout << "请输入点的个数:";
	cin >> count;
	Arr parr1(count);
	parr1.element(0).move(1, 2);
	parr1.element(1).move(2, 3);
	parr1.element(2).move(3, 4);


	Arr parr2(parr1);

	cout << "从parr1拷贝到parr2的点:" << endl;
	cout << "parr2的第一个元素:" << "(" << parr2.element(0).getX() << "," << parr2.element(0).getY() << ")" << endl;
	cout << "parr2的第二个元素:" << "(" << parr2.element(1).getX() << "," << parr2.element(1).getY() << ")" << endl;
	cout << "parr2的第三个元素:" << "(" << parr2.element(2).getX() << "," << parr2.element(2).getY() << ")" << endl;

	cout << "更新parr1的内容:" << endl;
	parr1.element(0).move(10, 20);
	parr1.element(1).move(20, 30);
	parr1.element(2).move(30, 40);

	cout << "更新parr1的内容之后parr2的内容:" << endl;
	cout << "parr2的第一个元素:" << "(" << parr2.element(0).getX() << "," << parr2.element(0).getY() << ")" << endl;
	cout << "parr2的第二个元素:" << "(" << parr2.element(1).getX() << "," << parr2.element(1).getY() << ")" << endl;
	cout << "parr2的第三个元素:" << "(" << parr2.element(2).getX() << "," << parr2.element(2).getY() << ")" << endl;

	return 0;

}

运行结果:

结果分析:

从这次的运行结果可以看出,程序实现的是深拷贝:更新parr1中的点不在影响parr2中的内粗空间,也不再引起错误。深拷贝的过程如图所示:

相关推荐
千里镜宵烛5 分钟前
Lua--数据文件和持久性
开发语言·junit·lua
乙己40721 分钟前
设计模式——单例模式(singleton)
java·c++·单例模式·设计模式
豐儀麟阁贵42 分钟前
5.6对象
java·开发语言
嵌入式小李.man1 小时前
linux中多路复用IO:select、poll和epoll
linux·c++
郝学胜-神的一滴1 小时前
QAxios研发笔记(二):在Qt环境下基于Promise风格简化Http的Post请求
开发语言·c++·笔记·qt·网络协议·程序人生·http
敲代码的嘎仔1 小时前
数据结构算法学习day3——二分查找
java·开发语言·数据结构·学习·程序人生·算法·职场和发展
m5655bj1 小时前
如何使用 Python 转换 Excel 工作表到 PDF 文档
开发语言·c#·excel
晨非辰2 小时前
《数据结构风云》:二叉树遍历的底层思维>递归与迭代的双重视角
数据结构·c++·人工智能·算法·链表·面试
ᐇ9592 小时前
Java核心概念深度解析:从包装类到泛型的全面指南
java·开发语言
逻极2 小时前
Rust之旅的起点:为什么选择Rust?
开发语言·后端·rust