返回

深浅对比,一文带你掌握浅拷贝与深拷贝

前端

浅拷贝与深拷贝:对象拷贝的基本原理

在编程世界中,对象拷贝是一种常见而关键的操作。它允许你创建对象的新副本,从而实现数据的隔离和共享。根据拷贝的深度,对象拷贝可以分为浅拷贝和深拷贝。

浅拷贝:浅尝辄止的拷贝

浅拷贝只复制对象本身的数据,而不会复制它所引用的其他对象。这就好比把一个装着文件的文件夹复制了一份,而没有复制文件夹里的文件。结果就是,浅拷贝后的副本与原对象指向相同的子对象。如果原对象所引用的子对象发生变化,则副本中的子对象也会随之变化。

深拷贝:彻底彻底的拷贝

深拷贝则完全不同。它会递归地复制对象及其所引用的所有子对象。这就好比把文件夹及其所有文件都复制了一份。结果就是,深拷贝后的副本与原对象完全独立,互不影响。即使原对象所引用的子对象发生变化,副本中的子对象也不会受到影响。

浅拷贝与深拷贝的应用场景

浅拷贝和深拷贝各有其适用的场景。一般来说,浅拷贝适合用于以下情况:

  • 当对象之间没有循环引用时,就像把文件夹复制出来不会造成无限循环一样。
  • 当对象所引用的子对象不需要隔离时,就像你不介意其他人修改你复制出来的文件夹里的文件一样。
  • 当对象所引用的子对象很少时,就像文件夹里只有几份文件,复制起来很容易一样。

深拷贝适合用于以下情况:

  • 当对象之间存在循环引用时,就像一个文件夹里包含着指向自己的链接一样。
  • 当对象所引用的子对象需要隔离时,就像你不想让别人修改你复制出来的文件夹里的文件一样。
  • 当对象所引用的子对象很多时,就像文件夹里塞满了文件,复制起来比较麻烦一样。

浅拷贝与深拷贝的实现方法

在不同的编程语言中,实现浅拷贝和深拷贝的方法有所不同。以下是一些常见的实现方法:

  • Python :使用copy.copy()函数进行浅拷贝,copy.deepcopy()函数进行深拷贝。
  • Java :使用clone()方法进行浅拷贝,使用Serializable接口和ObjectOutputStream/ObjectInputStream类进行深拷贝。
  • C++ :使用赋值运算符=进行浅拷贝,使用构造函数或memcpy()函数进行深拷贝。

代码示例

以下是一些浅拷贝和深拷贝的代码示例:

Python

class MyClass:
    def __init__(self, a, b):
        self.a = a
        self.b = b

obj1 = MyClass(1, 2)
obj2 = copy.copy(obj1)  # 浅拷贝
obj3 = copy.deepcopy(obj1)  # 深拷贝

print(obj1.a, obj1.b)  # 输出:1 2
print(obj2.a, obj2.b)  # 输出:1 2
print(obj3.a, obj3.b)  # 输出:1 2

obj1.a = 3
obj1.b = 4

print(obj1.a, obj1.b)  # 输出:3 4
print(obj2.a, obj2.b)  # 输出:1 2
print(obj3.a, obj3.b)  # 输出:1 2

Java

class MyClass {
    private int a;
    private int b;

    public MyClass(int a, int b) {
        this.a = a;
        this.b = b;
    }

    public MyClass clone() {
        try {
            return (MyClass) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

MyClass obj1 = new MyClass(1, 2);
MyClass obj2 = obj1.clone();  // 浅拷贝
MyClass obj3 = (MyClass) new ObjectInputStream(new ByteArrayInputStream(new ObjectOutputStream().writeObject(obj1))).readObject();  // 深拷贝

System.out.println(obj1.a + "," + obj1.b);  // 输出:1, 2
System.out.println(obj2.a + "," + obj2.b);  // 输出:1, 2
System.out.println(obj3.a + "," + obj3.b);  // 输出:1, 2

obj1.a = 3;
obj1.b = 4;

System.out.println(obj1.a + "," + obj1.b);  // 输出:3, 4
System.out.println(obj2.a + "," + obj2.b);  // 输出:1, 2
System.out.println(obj3.a + "," + obj3.b);  // 输出:1, 2

C++

class MyClass {
public:
    int a;
    int b;

    MyClass(int a, int b) {
        this->a = a;
        this->b = b;
    }

    MyClass(const MyClass& other) {
        this->a = other.a;
        this->b = other.b;
    }

    MyClass& operator=(const MyClass& other) {
        this->a = other.a;
        this->b = other.b;
        return *this;
    }
};

MyClass obj1(1, 2);
MyClass obj2 = obj1;  // 浅拷贝
MyClass obj3(obj1);  // 深拷贝

cout << obj1.a << "," << obj1.b << endl;  // 输出:1, 2
cout << obj2.a << "," << obj2.b << endl;  // 输出:1, 2
cout << obj3.a << "," << obj3.b << endl;  // 输出:1, 2

obj1.a = 3;
obj1.b = 4;

cout << obj1.a << "," << obj1.b << endl;  // 输出:3, 4
cout << obj2.a << "," << obj2.b << endl;  // 输出:1, 2
cout << obj3.a << "," << obj3.b << endl;  // 输出:1, 2

常见问题解答

  • 浅拷贝和深拷贝有什么区别?
    浅拷贝只复制对象本身的数据,而深拷贝则递归地复制对象及其所引用的所有子对象。
  • 什么时候应该使用浅拷贝?
    当对象之间没有循环引用,不需要隔离子对象或子对象很少时。
  • 什么时候应该使用深拷贝?
    当对象之间存在循环引用,需要隔离子对象或子对象很多时。
  • 如何实现浅拷贝和深拷贝?
    具体方法因编程语言而异,但通常浅拷贝使用赋值或克隆操作,深拷贝使用序列化或递归复制。
  • 浅拷贝和深拷贝在性能上有什么区别?
    深拷贝通常比浅拷贝慢,因为需要递归复制子对象。