深拷贝和浅拷贝

Object 类的clone() 方法 : 

  clone() 指创建并返回此对象的一个副本。(通过clone()方法返回一个新的对象,且新的对象的属性值从原对象拷贝)。

  clone() 是 Object 类的方法,所以每一个类都会从Object类继承此方法。

如何实现clone :  实现 Cloneable 接口 , 重写clone() 方法

  使用某个类的clone()方法,必须要实现 Cloneable 接口。否则会抛 java.lang.CloneNotSupportedException)因为Object 类本身不实现Cloneable接口,所以使用Object对象调用clone()方法时会抛出运行时异常 java.lang.CloneNotSupportedException。

  Object 类的 clone()方法是 protected修饰的,所以要重写clone()方法,并且将 protected 改为 public修饰。

@Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

 

 从内存分析深拷贝和浅拷贝的区别,以Car和Wheel为例

 

复制代码
package com.nemo.clone;
// 车轮 类
public class Wheel {
    private String name;

    public Wheel(String name) {
        this.name = name;
    }
    
    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
复制代码
复制代码
package com.nemo.clone;

public class Car implements Cloneable {
    private int id;
    private String type;
    //汽车 和 车轮 是组合关系
    private Wheel wheel;

    public Car (Wheel wheel) {
        this.wheel = wheel;
    }
    
    // 重写 clone() 方法
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public Wheel getWheel() {
        return wheel;
    }

    public void setWheel(Wheel wheel) {
        this.wheel = wheel;
    }
}
复制代码
复制代码
package com.nemo.clone;

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Wheel wheel = new Wheel("XXX"); // 创建一个车轮
        Car car = new Car(wheel);
        car.setId(123);
        car.setType("Audi");
        car.getWheel().setName("AAA"); //将车轮的name改为 AAA
        
        Car cloneCar = (Car)car.clone(); // clone 一个Car 
        cloneCar.setId(456); 
        cloneCar.setType("Benz");
        
        cloneCar.getWheel().setName("BBB"); // 通过cloneCar 将车轮的name 改为 BBB
        
        System.out.println(car);
        System.out.println(car.getId() + "  " + car.getType());
        System.out.println(car.getWheel());
        System.out.println(car.getWheel().getName());
        System.out.println("------------------------------------------");
        System.out.println(cloneCar);
        System.out.println(cloneCar.getId() + "  " + cloneCar.getType());
        System.out.println(cloneCar.getWheel());
        System.out.println(cloneCar.getWheel().getName());
    }
}
复制代码
复制代码
com.nemo.clone.Car@1fb8ee3
123  Audi
com.nemo.clone.Wheel@61de33
BBB
------------------------------------------
com.nemo.clone.Car@14318bb
456  Benz
com.nemo.clone.Wheel@61de33
BBB


从输出可以看出 car和拷贝出来的cloneCar的哈希码不同,car 的 id,type 和 cloneCar的id,type 也不同。(红色标识)
car的Wheel对象和cloneCar的Wheel对象哈希码相同,说明他们共享一个Wheel引用。所以当car更改了Wheel的name为"AAA"后cloneCar也更改Wheel的name为"BBB"最后car和cloneCar的name都输出BBB。(car和cloneCar共享一个引用 cloneCar后执行cloneCar.getWheel.setName("BBB") );

复制代码

(哈希码 : 指的是JAVA通过某算法将对象的地址生成16进制的字符串。用于唯一标识某对象)

 

以上为浅拷贝。

 

下面是深拷贝的例子 : 

复制代码
package com.nemo.clone;
// 车轮 类
public class Wheel implements Cloneable{
    private String name;

    public Wheel(String name) {
        this.name = name;
    }
    
    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    
    // 重写 clone() 方法
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
复制代码
复制代码
package com.nemo.clone;

public class Car implements Cloneable {
    private int id;
    private String type;
    //汽车 和 车轮 是组合关系
    private Wheel wheel;

    public Car (Wheel wheel) {
        this.wheel = wheel;
    }
    
    // 重写 clone() 方法
    @Override
    public Object clone() throws CloneNotSupportedException {
        Car object =  (Car)super.clone();
        object.wheel = (Wheel)wheel.clone();
        return object;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public Wheel getWheel() {
        return wheel;
    }

    public void setWheel(Wheel wheel) {
        this.wheel = wheel;
    }
}
复制代码
复制代码
package com.nemo.clone;

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Wheel wheel = new Wheel("XXX"); // 创建一个车轮
        Car car = new Car(wheel);
        car.setId(123);
        car.setType("Audi");
        car.getWheel().setName("AAA"); //将车轮的name改为 AAA
        
        Car cloneCar = (Car)car.clone(); // clone 一个Car 
        cloneCar.setId(456); 
        cloneCar.setType("Benz");
        
        cloneCar.getWheel().setName("BBB"); // 通过cloneCar 将车轮的name 改为 BBB
        
        System.out.println(car);
        System.out.println(car.getId() + "  " + car.getType());
        System.out.println(car.getWheel());
        System.out.println(car.getWheel().getName());
        System.out.println("------------------------------------------");
        System.out.println(cloneCar);
        System.out.println(cloneCar.getId() + "  " + cloneCar.getType());
        System.out.println(cloneCar.getWheel());
        System.out.println(cloneCar.getWheel().getName());
    }
}
复制代码
复制代码
输出结果 : 
com.nemo.clone.Car@1fb8ee3
123 Audi com.nemo.clone.Wheel@61de33 AAA ------------------------------------------ com.nemo.clone.Car@14318bb 456 Benz com.nemo.clone.Wheel@ca0b6 BBB

(红色标识的是与上一个例子不同的地方)
从输出结果可以看出 car和cloneCar的Wheel对象哈希码不同,说明car和cloneCar不再共享同一wheel对象。

复制代码

 

 

 

 (

  采用clone()方法这种方式拷贝对象,其实并不好用。每一个类都需要实现cloneable接口,重写clone方法。当类的等级结构很深的时候会很乱。

  另一种拷贝对象的方法是 采用序列化的方式。

   // JAVA创建对象的几种方式

)

<think>嗯,用户想了解C++中的深拷贝浅拷贝的区别以及实现方法。首先,我需要回忆一下这两个概念的基本定义。浅拷贝应该是指简单的复制指针,导致两个对象指向同一块内存,而深拷贝则是复制指针所指向的数据本身,这样两个对象就独立了。 根据用户提供的引用内容,引用[1]里提到浅拷贝只拷贝指针,深拷贝会拷贝内容,默认的拷贝构造函数是浅拷贝,需要自己实现深拷贝。引用[2][3]给出了具体的代码示例,展示了如何通过自定义拷贝构造函数来实现深拷贝,比如在Person类中为Name或m_height重新分配内存并复制数据。引用[4]中的Array类也演示了类似的深拷贝实现,即在拷贝构造函数中新建内存空间并复制数据。 接下来,我需要将这些信息组织成一个清晰的回答。首先解释两者的区别,用比喻的话,浅拷贝像快捷方式,深拷贝像复制文件,这样用户容易理解。然后,说明默认拷贝构造函数的问题,比如会导致重复释放内存,出现错误。接着,用用户提供的代码例子来说明如何手动实现深拷贝,比如在拷贝构造函数中分配新内存,并复制数据。可能还需要提到析构函数的重要性,确保深拷贝的对象能正确释放内存。 用户可能还关心什么时候需要深拷贝,应该是在类中有指针成员指向动态分配的内存时。这时候必须自己实现深拷贝,否则会出现问题。同时,需要注意在实现深拷贝时,除了拷贝构造函数,可能还需要重载赋值运算符,但用户的问题可能没有提到,所以可能暂时不需要展开,但如果有相关问题可以提到。 最后,生成的相关问题需要围绕实现深拷贝的方法、常见错误场景以及如何避免等问题。这样用户如果有进一步的问题,可以继续查阅相关内容。确保回答中正确引用提供的资料,比如引用[1]、[3]中的例子来说明浅拷贝的问题深拷贝的实现步骤。</think>### C++深拷贝浅拷贝的区别及实现方式 #### 一、核心概念 1. **浅拷贝** - 仅复制对象的指针成员变量,拷贝后新旧对象的指针**指向同一块内存地址**,例如: ```cpp //示例代码片段 m_height = p.m_height; //浅拷贝直接复制指针[^3] ``` - 类似文件快捷方式,多个指针共享同一资源,可能导致**重复释放内存**的崩溃问题[^1] 2. **深拷贝** - 为指针成员**创建独立的内存空间**,完全复制原对象的数据内容: ```cpp m_height = new int(*p.m_height); //新建堆空间并复制数据[^3] ``` - 类似完整文件复制,新旧对象完全独立,避免资源冲突[^1] #### 二、实现方式对比 | 特征 | 浅拷贝 | 深拷贝 | |--------------|---------------------------|---------------------------| | 内存分配 | 共享原对象内存 | 新建独立内存空间 | | 实现复杂度 | 编译器默认提供 | 需手动实现拷贝构造函数 | | 资源管理风险 | 易出现重复释放 | 内存管理安全 | | 典型应用场景 | 无指针成员的简单对象 | 包含指针/动态资源的对象 | #### 三、关键实现步骤(以类`person`为例) 1. **定义深拷贝构造函数** ```cpp person(const person& p) { m_age = p.m_age; m_height = new int(*p.m_height); //关键:新建堆内存并复制数据 } ``` 2. **配套析构函数** ```cpp ~person() { delete m_height; //确保正确释放深拷贝分配的内存 m_height = nullptr; } ``` 3. **错误示例(浅拷贝陷阱)** 若直接使用默认拷贝构造函数: ```cpp person p2(p1); //此时p2.m_height与p1.m_height指向同一地址 ``` 析构时会导致**重复释放同一内存地址**,引发程序崩溃[^1] #### 四、应用场景分析 - **必须使用深拷贝**:类包含指针成员且指向动态分配内存时(如字符串、数组等) - **可使用浅拷贝**:仅包含基本数据类型或没有资源所有权的指针时 - **典型案例**:自定义容器类(如动态数组`Array`类[^4])、包含文件句柄的类
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值