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中的内粗空间,也不再引起错误。深拷贝的过程如图所示:

相关推荐
快乐就好ya39 分钟前
Java多线程
java·开发语言
CS_GaoMing1 小时前
Centos7 JDK 多版本管理与 Maven 构建问题和注意!
java·开发语言·maven·centos7·java多版本
麻辣韭菜2 小时前
网络基础 【HTTP】
网络·c++·http
阿史大杯茶2 小时前
Codeforces Round 976 (Div. 2 ABCDE题)视频讲解
数据结构·c++·算法
2401_858120532 小时前
Spring Boot框架下的大学生就业招聘平台
java·开发语言
转调2 小时前
每日一练:地下城游戏
开发语言·c++·算法·leetcode
Java探秘者2 小时前
Maven下载、安装与环境配置详解:从零开始搭建高效Java开发环境
java·开发语言·数据库·spring boot·spring cloud·maven·idea
2303_812044463 小时前
Bean,看到P188没看了与maven
java·开发语言
秋夫人3 小时前
idea 同一个项目不同模块如何设置不同的jdk版本
java·开发语言·intellij-idea
不穿格子衬衫3 小时前
常用排序算法(下)
c语言·开发语言·数据结构·算法·排序算法·八大排序