[C++] Deep copy and shallow copy ④ (deep copy example)





1. Deep copy example




1. Shallow copy problem


In the previous blog [C++] Deep Copy and Shallow Copy ③ (Shallow Copy Memory Analysis) , shallow copy was used to assign the original object Students to the copy object Students s2;

Use the default copy constructor generated by the C++ compiler for object assignment, and this copy is a shallow copy;


Two problems arise when using shallow copies:

  • Shallow copy causes two objects to hold the same pointer. If the data pointed to by the copied object pointer is modified, the data pointed to by the original object pointer will also be modified together;
  • When destructing, the pointers of both objects need to be released. When the second pointer is released, the pointer has already been released. If an already released pointer is released repeatedly, an error will be reported directly;

Insert image description here


2. Implement deep copy yourself


In the above shallow copy, only the pointer variable is copied, and memory is not re-allocated for the pointer member variable of the new object, resulting in a series of subsequent problems;

If you want to implement a deep copy operation, you need to use the copy constructor. Once you encounter a pointer member variable, immediately measure the size of the heap memory allocated by the pointer, and then save the data to be copied in the new memory;


For the m_name pointer type member variable in the Student class to be copied, the deep copy process is as follows:

  • First, get the length of the string pointed to by the char* type pointer, and use the strlen function to measure the size of the heap memory pointed to by the pointer;
// 获取字符串长度
int len = strlen(s.m_name);
  • Then, allocate memory for the object's member variable m_name pointer. Note that this is allocating memory for the char* type string, and also allocating memory for the '\0' character at the end of the string;
// 为 m_name 成员分配内存 
// 注意还要为字符串结尾的 '\0' 字符分配内存
m_name = (char*)malloc(len + 1);
  • Finally, use the strcpy function to copy the string content. In C++, you need to add the #define _CRT_SECURE_NO_WARNINGS macro definition, otherwise an error will be reported;
// 拷贝字符串
// C++ 中使用该函数需要
// 添加 #define _CRT_SECURE_NO_WARNINGS 宏定义
if (m_name != NULL)
{
    
    
	strcpy(m_name, s.m_name);
}

Code example of custom deep copy constructor:

	// 拷贝构造函数
	// 执行 Student s2 = s; 代码时调用该函数
	// 自己实现 深拷贝 操作
	Student(const Student& s)
	{
    
    
		// 获取字符串长度
		int len = strlen(s.m_name);

		// 为 m_name 成员分配内存 
		// 注意还要为字符串结尾的 '\0' 字符分配内存
		m_name = (char*)malloc(len + 1);

		// 拷贝字符串
		// C++ 中使用该函数需要
		// 添加 #define _CRT_SECURE_NO_WARNINGS 宏定义
		if (m_name != NULL)
		{
    
    
			strcpy(m_name, s.m_name);
		}

		// 为 m_age 成员设置初始值
		m_age = s.m_age;

		cout << "调用拷贝构造函数" << endl;
	}




2. Deep copy complete code example



In the following code, the copy constructor of deep copy is customized;

When the code is executed Student s2 = s;, the custom deep copy constructor is automatically called. During the copy, the memory space is re-allocated in the heap memory for the pointer member, and the string data is copied;

Execute the following code to modify the copied object alone without affecting the original object;

// 修改 s2 对象
strcpy(s2.m_name, "Jey");

During the final destruction, since the char* m_name; pointer member variables of the copied object and the original object point to different memory spaces respectively, the destruction of the two objects will not affect the destruction of the pointer members of the other object;


Complete code example:

#define _CRT_SECURE_NO_WARNINGS

#include "iostream"
using namespace std;

class Student
{
    
    
public:

	// 有参构造函数
	Student(int age, const char* name)
	{
    
    
		// 获取字符串长度
		int len = strlen(name);

		// 为 m_name 成员分配内存 
		// 注意还要为字符串结尾的 '\0' 字符分配内存
		m_name = (char*)malloc(len + 1);

		// 拷贝字符串
		// C++ 中使用该函数需要
		// 添加 #define _CRT_SECURE_NO_WARNINGS 宏定义
		if (m_name != NULL)
		{
    
    
			strcpy(m_name, name);
		}
			
		// 为 m_age 成员设置初始值
		m_age = age;

		cout << "调用有参构造函数" << endl;
	}

	// 拷贝构造函数
	// 执行 Student s2 = s; 代码时调用该函数
	// 自己实现 深拷贝 操作
	Student(const Student& s)
	{
    
    
		// 获取字符串长度
		int len = strlen(s.m_name);

		// 为 m_name 成员分配内存 
		// 注意还要为字符串结尾的 '\0' 字符分配内存
		m_name = (char*)malloc(len + 1);

		// 拷贝字符串
		// C++ 中使用该函数需要
		// 添加 #define _CRT_SECURE_NO_WARNINGS 宏定义
		if (m_name != NULL)
		{
    
    
			strcpy(m_name, s.m_name);
		}

		// 为 m_age 成员设置初始值
		m_age = s.m_age;

		cout << "调用拷贝构造函数" << endl;
	}

	~Student()
	{
    
    
		// 销毁 name 指向的堆内存空间
		if (m_name != NULL)
		{
    
    
			free(m_name);
			m_name = NULL;
		}
		cout << "调用析构函数" << endl;
	}

	// 该类没有定义拷贝构造函数 , C++ 编译器会自动生成默认的拷贝构造函数

	// 打印类成员变量
	void toString()
	{
    
    
		cout << "m_age = " << m_age << " , m_name = " << m_name << endl;
	}

public:
	int m_age;
	char* m_name;
};

int main()
{
    
    
	// 调用有参构造函数 , 创建 Student 实例对象
	Student s(18, "Tom");
	// 打印 Student 实例对象成员变量值
	s.toString();

	// 声明 Student 对象 s2 , 并使用 s 为 s2 赋值
	// 该操作会调用 默认的拷贝构造函数 
	// C++ 编译器提供的拷贝构造函数 只能进行浅拷贝
	Student s2 = s;
	s2.toString();

	// 修改 s2 对象
	strcpy(s2.m_name, "Jey");
	s.toString();
	s2.toString();

	// 执行时没有问题 , 两个对象都可以正常访问
	// 但是由于拷贝时 执行的是浅拷贝 
	// 浅拷贝 字符串指针时 , 直接将指针进行拷贝 , 没有拷贝具体的值
	// s 和 s2 的 m_name 成员是同一个指针
	// 如果析构时 , 先析构 s2 , 将指针释放了 
	// 之后再析构 s 时 发现 继续释放 被释放的指针 , 报错了



	// 控制台暂停 , 按任意键继续向后执行
	system("pause");
	return 0;
}

Results of the :

调用有参构造函数
m_age = 18 , m_name = Tom
调用拷贝构造函数
m_age = 18 , m_name = Tom
m_age = 18 , m_name = Tom
m_age = 18 , m_name = Jey
请按任意键继续. . .
调用析构函数
调用析构函数

Y:\002_WorkSpace\002_VS\HelloWorld\HelloWorld\Debug\HelloWorld.exe (进程 7480)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

Insert image description here

Guess you like

Origin blog.csdn.net/han1202012/article/details/132951373