IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++:浅拷贝与深拷贝 -> 正文阅读

[C++知识库]C++:浅拷贝与深拷贝

1 浅拷贝

简单的赋值拷贝操作

2 深拷贝

在堆区重新申请空间,进行拷贝操作

3 对比:是否delete堆区的数据

栈区的数据在使用完毕后由编译器自动释放对应的内存;堆区的数据由程序员自己开辟,自己释放,即在使用完毕后不会自动释放。如果忘记对堆区的数据释放,会造成内存泄漏,直到整个程序执行完毕后,堆区的数据才被回收。

分别在栈区开辟一块内存,存放变量a;在堆区开辟一块内存,存放成员变量b。

错误示范:没有手动释放堆区的数据

#include<iostream>

using namespace std;

int *g_a;
int *g_b;

void test()
{
	int a = 10;				//a存放于栈区,test函数执行完毕后,由编译器自动释放内存
	int *b = new int(20);	//b存放与堆区,test函数执行完毕后,编译器不会自动释放,需要用户自己回收 delete,否则会造成内存泄漏
	cout << "a = " << a << endl;
	cout << "b = " << *b << endl;

	g_a = &a;
	g_b = b;

	cout << "test函数执行完毕!" << endl;
}

int main()
{
	test();

	//由于栈区中的a在test函数执行完毕后自动释放,所以a对应的内存的值不为10
	cout << "a = " << *g_a << endl;
	cout << "a = " << *g_a << endl;
	cout << "a = " << *g_a << endl;

	//由于堆区中的b在test函数执行完毕后没有被释放,所以b对应的内存的值仍为20
	cout << "b = " << *g_b << endl;
	cout << "b = " << *g_b << endl;
	cout << "b = " << *g_b << endl;

	return 0;
}

输出结果:

a = 10
b = 20
test函数执行完毕!
a = -858993460
a = -858993460
a = -858993460
b = 20
b = 20
b = 20

正确示范:堆区的数据使用完毕后,执行delete操作

#include<iostream>

using namespace std;

int *g_a;
int *g_b;

void test()
{
	int a = 10;				//a存放于栈区,test函数执行完毕后,由编译器自动释放内存
	int *b = new int(20);	//b存放与堆区,test函数执行完毕后,编译器不会自动释放,需要用户自己回收 delete,否则会造成内存泄漏
	cout << "a = " << a << endl;
	cout << "b = " << *b << endl;

	g_a = &a;
	g_b = b;

	delete b;	//释放堆区中的数据 b

	cout << "test函数执行完毕!" << endl;
}

int main()
{
	test();

	//由于栈区中的a在test函数执行完毕后自动释放,所以a对应的内存的值不为10
	cout << "a = " << *g_a << endl;
	cout << "a = " << *g_a << endl;
	cout << "a = " << *g_a << endl;

	//堆区中的b在test函数执行完毕后,被delete释放,所以b对应的内存的值不为20
	cout << "b = " << *g_b << endl;
	cout << "b = " << *g_b << endl;
	cout << "b = " << *g_b << endl;

	return 0;
}

输出结果:

a = 10
b = 20
test函数执行完毕!
a = -858993460
a = -858993460
a = -858993460
b = -572662307
b = -572662307
b = -572662307

4 浅拷贝与深拷贝

4.1 浅拷贝造成的错误

代码:

#include <iostream>

using namespace std;

class Person
{
public:
	Person(int age,int height)
	{
		m_age = age;
		m_height = new int (height);	//在堆区开辟一块内存,存放成员变量 m_height;需要手动释放,即需要自定义一个析构函数
		cout << "Person 有参构造函数调用" << endl;
	}
	//默认拷贝函数,如果不提供拷贝构造,编译器就会提供下面的默认拷贝函数,为浅拷贝
	Person(const Person& p)
	{
		m_age = p.m_age;
		m_height = p.m_height;	//浅拷贝,只复制p.m_height的地址,复制前后的地址指向同一块内存
		cout << "Person 默认拷贝构造函数调用" << endl;
	}

	//自定义的析构函数,用于回收堆区的数据
	~Person()
	{
		//析构,将堆区开辟的内存释放
		//如果m_height不为空指针,则释放m_height指向的内存,并将m_height赋值为空指针。
		if (m_height != NULL)
		{
			delete m_height;
			m_height = NULL;
		}

		cout << "Person 析构函数调用" << endl;
	}

	int m_age;		//年龄(存放于栈区)
	int *m_height;	//身高(存放于堆区)
};


void test()
{
	Person p1(18, 180);

	Person p2(p1);

	cout << "p1的年龄: " << p1.m_age << " 身高: " << *p1.m_height << endl;

	cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl;
}


int main()
{
	test();

	return 0;
}

输出结果:

Person 有参构造函数调用
Person 拷贝构造函数调用
p1的年龄: 18 身高: 180
p2的年龄: 18 身高: 180
Person 析构函数调用

运行中断,报错如下。
在这里插入图片描述
这就是浅拷贝带来的问题!

4.2 如果类成员有在堆区开辟的,一定要自己提供拷贝构造函数(深拷贝),防止浅拷贝带来的问题

代码:

#include <iostream>

using namespace std;

class Person
{
public:

	//有参构造
	Person(int age,int height)
	{
		m_age = age;
		m_height = new int (height);	//在堆区开辟一块内存,存放成员变量 m_height;需要手动释放,即需要自定义一个析构函数
		cout << "Person 有参构造函数调用" << endl;
	}

	//自定义的拷贝构造函数
	Person(const Person& p)
	{
		m_age = p.m_age;
		m_height = new int(*p.m_height);	//深拷贝,重新在堆区开辟一块内存存放拷贝后的数据
		cout << "Person (自定义)拷贝构造函数调用" << endl;
	}
	
	//自定义的析构函数,用于回收堆区的数据
	~Person()
	{
		//析构,将堆区开辟的内存释放
		//如果m_height不为空指针,则释放m_height指向的内存,并将m_height赋值为空指针。
		if (m_height != NULL)
		{
			delete m_height;
			m_height = NULL;
		}

		cout << "Person 析构函数调用" << endl;
	}

	int m_age;		//年龄(存放于栈区)
	int *m_height;	//身高(存放于堆区)
};


void test()
{
	Person p1(18, 180);

	Person p2(p1);

	cout << "p1的年龄: " << p1.m_age << " 身高: " << *p1.m_height << endl;

	cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl;
}


int main()
{
	test();

	return 0;
}

输出结果:

Person 有参构造函数调用
Person (自定义)拷贝构造函数调用
p1的年龄: 18 身高: 180
p2的年龄: 18 身高: 180
Person 析构函数调用
Person 析构函数调用

4.3 浅拷贝与深拷贝图解

浅拷贝,只是将地址复制,拷贝前后两份地址指向同一块内存,p1调用析构函数,会立马释放该内存空间;当p2调用析构函数时,没有内存可以释放,故发生错误。
在这里插入图片描述
深拷贝,在堆区开辟一块内存,存放拷贝后的数据,这样,p1、p2调用析构函数时就不会互相干扰。
在这里插入图片描述

如果还是不明白,请参考https://www.bilibili.com/video/BV1et411b73Z?p=110

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-28 22:43:27  更:2021-12-28 22:44:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:11:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码