C++: Shallow Copy vs Deep Copy

1 Shallow copy

Simple assignment copy operation

2 deep copy

Re-apply for space in the heap area and perform a copy operation

3 Comparison: whether to delete the data in the heap area

The data in the stack area is automatically released by the compiler after the corresponding memory is used; the data in the heap area is opened up by the programmer and released by himself, that is, it will not be released automatically after use. If you forget to release the data in the heap area, it will cause a memory leak, and the data in the heap area will not be reclaimed until the entire program is executed.

Open up a piece of memory in the stack area to store variable a; open up a piece of memory in the heap area to store member variable b.

Error demonstration: data in the heap area is not manually released

#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;
}

Output result:

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

Correct demonstration: After the data in the heap area is used, execute the delete operation

#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;
}

Output result:

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

4 Shallow copy and deep copy

4.1 Errors caused by shallow copy

Code:

#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;
}

Output result:

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

The operation is interrupted and the following error is reported.
insert image description here
That's the problem with shallow copies!

4.2 If the class members are opened in the heap area, you must provide your own copy constructor (deep copy) to prevent problems caused by shallow copy

Code:

#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;
}

Output result:

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

4.3 Shallow copy and deep copy diagram

Shallow copy just copies the address. The two addresses before and after the copy point to the same piece of memory. When p1 calls the destructor, the memory space will be released immediately; when p2 calls the destructor, there is no memory to release, so an error occurs.
insert image description here
Deep copy, open up a piece of memory in the heap area to store the copied data, so that p1 and p2 will not interfere with each other when calling the destructor.
insert image description here

If you still don't understand, please refer to https://www.bilibili.com/video/BV1et411b73Z?p=110

Guess you like

Origin blog.csdn.net/weixin_46098577/article/details/122179878