前言:在 C++ 中,当一个对象通过赋值或拷贝即将成为另一个对象的副本时,会发生“浅拷贝”或“深拷贝”。
什么是浅拷贝?
浅拷贝是指两个对象共享同一个内存地址,即将源对象的数据成员的地址赋给了目标对象,当源对象和目标对象被销毁时,会发生重复释放的问题,导致程序崩溃。
同一类型的对象之间可以赋值,使得两个对象的成员变量的值相同,两个对象仍然是独立的两个对象,这种情况被称为浅拷贝.
一般情况下,浅拷贝没有任何副作用,但是当类中有指针,并且指针指向动态分配的内存空间,析构函数做了动态内存释放的处理,会导致内存问题。
看图理解:
下面是一个浅拷贝的示例:
class Person {
public:
int mAge;
char* pName;
Person(const char* n, int a) : name(new char[strlen(n) + 1]), mAge(a) {
strcpy(pName, n);
}
// 浅拷贝构造函数
Person(const Person & p) : age(p.mAge), name(p.pName) {}
};
在上述代码中,我们定义了一个 Person
类,包含了一个 char
类型的指针成员 pName 和一个 int
类型的成员 mAge。 在浅拷贝的构造函数中,会将源对象的地址直接赋值给目标对象,这样就会导致两个对象共享同一块内存,当其中一个对象被销毁时,会导致另一个对象的指针成员变量指向一块已被释放的内存,从而引发程序崩溃。
什么是深拷贝?
深拷贝是指在拷贝对象时,会开辟一块新的内存空间来存储数据,并将源对象的数据成员的内容复制到目标对象的数据成员中,这样就不会有重复释放的问题。
当类中有指针,并且此指针有动态分配空间,析构函数做了释放处理,往往需要自定义拷贝构造函数,自行给指针动态分配空间,深拷贝。
看图理解:
下面是一个深拷贝的示例:
class Person {
public:
int mAge;
char* pName;
Person(const char* n, int a) : name(new char[strlen(n) + 1]), mAge(a) {
strcpy(pName, n);
}
// 深拷贝构造函数
Person(const Person & p) : age(p.mAge), name(new char[strlen(p.pName) + 1]) {
strcpy(name, p.pName);
}
};
在上述代码中,我们使用深拷贝的方式创建一个对象,将源对象的数据成员的内容复制到了目标对象中,防止了重复释放的问题。
如何选择?
当对象中只包含了基本数据类型成员时,可以使用浅拷贝;当对象中包含了指针类型成员时,需要使用深拷贝。
在实践中,我们需要根据具体的情况选择不同的拷贝方式。当对象中存在指针类型成员时,需要确保在拷贝时不会共享同一个地址,否则需要使用深拷贝。此外,在使用深拷贝时,还要注意好释放内存,避免内存泄漏的问题。
下面以一个包含指针类型成员变量的例子来作为说明:
class Employee {
public:
Employee(const char* n, int a, const char* p) : name(new char[strlen(n) + 1]), age(a), position(new char[strlen(p) + 1]) {
strcpy(name, n);
strcpy(position, p);
}
~Employee() {
delete[] name;
delete[] position;
}
// 浅拷贝构造函数
Employee(const Employee& other) : name(other.name), age(other.age), position(other.position) {}
// 深拷贝构造函数
Employee(const Employee& other) : age(other.age), name(new char[strlen(other.name) + 1]), position(new char[strlen(other.position) + 1]) {
strcpy(name, other.name);
strcpy(position, other.position);
}
private:
char* name;
int age;
char* position;
};
在上述代码中,我们定义了 Employee
类,其中包含了三个成员变量,包括两个 char
类型的指针类型成员变量 name
和 position
,以及一个 int
类型的基本数据类型成员变量 age
。
我们可以看到,如果要进行浅拷贝,我们只需要将源对象的指针类型成员变量的地址复制给目标对象的指针类型成员变量即可,但是这样做有一个问题就是源对象和目标对象会共享指针指向的内存空间,当源对象被销毁时,目标对象持有的指针就会指向一块已经被释放的内存空间,从而导致程序崩溃,因此浅拷贝对这种类型的成员变量并不适用。
对于包含指针类型成员变量的对象,常用的拷贝方式是深拷贝。这种拷贝方式会为目标对象重新分配一块内存,并将源对象的数据成员的内容复制到目标对象的数据成员中,从而避免了重复释放的问题。在上述代码中,我们使用了深拷贝的方式来创建目标对象。
总结:
在 C++ 的开发中,深拷贝和浅拷贝都是很重要的概念,具体应该根据对象的成员变量类型来选择拷贝方式,并且在使用深拷贝时需要注意好内存的释放问题。建议当对象中包含了指针类型成员时,使用深拷贝;当对象中只包含了基本数据类型成员时,可以使用浅拷贝。