谈谈拷贝这件小事

栏目: PHP · 发布时间: 5年前

内容简介:当在使用某个对象,而需要对该对象进行大量操作,或者在新的上下文环境中复用该对象的部分或全部数据时,需要对其进行拷贝操作。那么结合代码来看看从浅拷贝到深拷贝发生了什么上面创建了一个people类,然后实例化了p1,用赋值的方式 创建了p2 这样就得到了两个名字,年龄,性别都一样的人。那么当我们修改p1或p2时都会改变name

当在使用某个对象,而需要对该对象进行大量操作,或者在新的上下文环境中复用该对象的部分或全部数据时,需要对其进行拷贝操作。

拷贝的深浅

  • 我们日常所使用的拷贝,如一个文件夹,或者视频,音乐的拷贝,使用时可以发现这种拷贝是真正的生成了一个新的文件,占用了部分内存的。那么这种就是深拷贝,也就是在计算机中重新开辟了一块新的内存地址用于存放对象。 而什么是浅拷贝,浅拷贝只是拷贝了基本的数据类型,而引用数据类型,复制后也是会发生引用,浅拷贝只是指向被复制的内存地址。如果引用数据类型的对象发生了改变,那么浅拷贝出来的对象也会改变

由浅入深

那么结合代码来看看从浅拷贝到深拷贝发生了什么

<?php

class People {

    protected $_name = '张三';
    protected $_sex = '男';
    protected $_age = '18';

    /**
     * return Name
     */
    public function getName() {
        return $this-> _name;
    }
    /**
     * set name
     */
    public function setName($name) {
        $this->_name = (string)$name;
        return $this;
    }
}
$p1 = new People();
$p2 = $p1;
复制代码

上面创建了一个people类,然后实例化了p1,用赋值的方式 创建了p2 这样就得到了两个名字,年龄,性别都一样的人。那么当我们修改p1或p2时都会改变name

echo $p1->getName();
echo $p2->getName();
//p2改名
$p2->setName('李四');
echo $p1->getName();
echo $p2->getName();
//p1改名
$p1->setName('王五');
echo $p1->getName();
echo $p2->getName();
复制代码

输出为:张三张三李四李四王五王五

这里对象的赋值和传值都是以引用的方式。名字虽然不同但指的是同一个人。所以这种拷贝相当于一个人有个名字和外号,人还是这个人,这种拷贝不是我所想要的。那么换种方式。用clone来复制对象。

clone函数

$p1 = new People();
$p2 = clone $p1;
echo $p1->getName();
echo $p2->getName();
//p2改名
$p2->setName('李四');
echo $p1->getName();
echo $p2->getName();
//p1改名
$p1->setName('王五');
echo $p1->getName();
echo $p2->getName();
复制代码

那么这段代码用clone关键字复制p1对象,现在这个p1对象得到了这个真正的拷贝p2,p1和p2改名时分别都能改成功,而p1和p2都属于不同的对象,都是独立的个体了,如果p1有其他的关系,那么如何呢,就如同你克隆了一个人,尽管这个克隆人和本体的基本属性相同,但会有相同的记忆吗会有复杂的社会关系吗? 显然并不会

那么假如这个张三这个人有同学这个类,现在如何让克隆体也有这个同学类呢。下面精简一下代码

class People {

    public $name = '张三';
    public $mate;
    /**
     * 构造函数中加载同学对象
     */
    public function __construct()
    {
        $this->mate = new Classmate();
    }
}
/**
 * 同学类
 */
class Classmate {
    public $name ="王五";
}
$p1 = new People();
$p2 = clone $p1;
$p2 ->name = "ll";
echo $p1->name;
echo $p2->name;
$p2->mate->name = "ll";
echo $p1->mate->name;
复制代码

然后可以发现p2可以改名字也就是p2的普通属性实现了深拷贝 而mate对象属性中的名字也会改变,存在一定的问题

一般有两种解决方法:

1.重写clone函数

class People {

    public $name = '张三';
    public $mate;
    /**
     * 构造函数中加载同学对象
     */
    public function __construct()
    {
        $this->mate = new Classmate();
    }
    
    //重写clone函数
    public function __clone() {
        $this->mate = clone $this->mate;
    }
}
/**
 * 同学类
 */
class Classmate {
    public $name ="王五";
}
$p1 = new People();
$p2 = clone $p1;
$p2 ->name = "ll";
echo $p1->name;
echo $p2->name;
$p2->mate->name = "ll";
echo $p1->mate->name;//输出还是王五
复制代码

这样就可以解决了,但是如果classmate中有很多属性,或者people类要引入很多类,那么这样的重写clone函数就会变得很麻烦

2序列化和反序列化

这种方法不用修改函数比较简单

<?php

class People {

    public $name = '张三';
    public $mate;
    /**
     * 构造函数中加载同学对象
     */
    public function __construct()
    {
        $this->mate = new Classmate();
    }


}
/**
 * 同学类
 */
class Classmate {
    public $name ="王五";
}
$p1 = new People();
$p2 = serialize($p1);
$p2 = unserialize($p2);
$p2 ->name = "ll";

$p2->mate->name = "ll";

echo $p1->mate->name;

复制代码

还可以用json_encode之后再json_decode,实现赋值和法二一样 


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

代码阅读方法与实践

代码阅读方法与实践

斯平内利斯 / 赵学良 / 清华大学出版社 / 2004-03-01 / 45.00元

代码阅读有自身的一套技能,重要的是能够确定什么时候使用哪项技术。本书中,作者使用600多个现实的例子,向读者展示如何区分好的(和坏的)代码,如何阅读,应该注意什么,以及如何使用这些知识改进自己的代码。养成阅读高品质代码的习惯,可以提高编写代码的能力。 阅读代码是程序员的基本技能,同时也是软件开发、维护、演进、审查和重用过程中不可或缺的组成部分。本书首次将阅读代码作为一项独立课题......一起来看看 《代码阅读方法与实践》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换