[C++] Deep copy and shallow copy ② (The default copy constructor is shallow copy | Code example - problems caused by shallow copy)





1. The default copy constructor is a shallow copy




1. Default copy constructor


If there is no copy constructor defined in the C++ class , the C++ compiler will automatically provide a "default copy constructor" for the class, and perform a simple copy operation on the member variables in the function;

The "default copy constructor" is used to create a new object as a copy of an existing object. Its function is to copy the member variables of the existing object to the new object;

When a class object is created and assigned to another class object, the default copy constructor is automatically called;


2. The default copy constructor is a shallow copy mechanism


The default copy constructor automatically generated by the C++ compiler for a class is a shallow copy, which can only copy the top-level member variable value. If the member variable is a reference or pointer, the data in the class or memory space it points to cannot be copied;


If the copy constructor is not defined, the above mechanism will be triggered;

When the following code call occurs, the parameterized constructor is first called to create an original object s.

Then assign the value of the s object to the s2 object. At this time, the copy constructor is called.

Since there is no copy constructor defined, the default copy constructor of the C++ compiler is used, and the copy performed is a shallow copy;

The string pointer only copies the value of the pointer, but does not copy the specific content of the string;

	// 调用有参构造函数 , 创建 Student 实例对象
	Student s(18, "Tom");

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




2. Code example - problems caused by shallow copy



In the code below,

In the defined Student class, a parameterized constructor and destructor are defined.

There is no copy constructor defined, so the C++ compiler generates a default copy constructor for it.

The default copy constructor is a shallow copy;


Analyze the code below to create two Student objects:

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

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

Student s(18, "Tom")It is to call the parameterized constructor, create the Student instance object, and call to s.toString()print the above object. The printing result is:

m_age = 18 , m_name = Tom

Student s2 = sIn the code, declare the Student object s2 and use s to assign a value to s2. This operation will call the default copy constructor. The copy constructor provided by the C++ compiler can only perform shallow copy, so the printed value is the same;

m_age = 18 , m_name = Tom

Analyze and modify the copy object code:

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

strcpy(s2.m_name, "Jey")In the code, the content pointed by the copy object pointer is modified, and "Tom" is changed to "Jey". After modifying the content pointed by the pointer, the m_name member value of the copied object and the original object becomes "Jey";


Both the copied object and the original object use the same pointer, so you need to pay attention when destructing. You cannot free the same pointer repeatedly, otherwise an error will be reported;


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()
	{
    
    
		// 销毁 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;
}

Execution results: After execution, the following content is printed:

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

Insert image description here
Press any key to continue execution backwards. After calling the first destructor, try to call the second destructor again, and an error will be reported;

Insert image description here

Guess you like

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