现在的位置: 首页 > 综合 > 正文

深拷贝与浅拷贝 AS3 数组concat

2017年12月07日 ⁄ 综合 ⁄ 共 2782字 ⁄ 字号 评论关闭

记得学C/C++的时候,一直跟指针纠结过。在OOP中虽然用引用代替了指针,但倘若不注意,还是会被小绊一下的。下面以AS3中的数组为例,谈谈对象的“深浅”拷贝。

先来看Demo1,这个是没有使用拷贝的情况下,直接进行赋值的。
Demo1:
//复本只保存了引用
var arrO:Array=[10,20,30];
trace("源数组:"+arrO);
var arrCopy:Array=arrO;
trace("复本:"+arrCopy);
arrCopy[0]=50;
trace("更改了arrCopy[0]后,源数组:"+arrO+",复本:"+arrCopy);
结果当然很容易猜到:
源数组:10,20,30
复本:10,20,30
更改了arrCopy[0]后,源数组:50,20,30,复本:50,20,30
arrCopy=arrO这个赋值只是将arr0这个指向数组的引用赋给了arrCopy,arrCopy与arrO都同指向同一块内存区域,所以在不管是通过arrCopy或是arrO修改了元素的值,改动的都是同一个区域。
Demo2:在AS3的Array类型提供了两个用于数组间操作的方法,concat用于连接数组,slice用于返回数组中指定范围的元素,这两个方法的返回值都是数组,而且其操作不会影响到原始数组。两个方法参数为空,即可以达到复制数组的效果。
//使用不带参数的concat/slice返回数组中的所有元素副本
var arrO:Array=[10,20,30];
trace("源数组:"+arrO);
var arrCopy:Array=arrO.slice();
trace("复本:"+arrCopy);
arrCopy[0]=50;
trace("更改了arrCopy[0]后,源数组:"+arrO+",复本:"+arrCopy);
结果:
源数组:10,20,30
复本:10,20,30
更改了arrCopy[0]后,源数组:10,20,30,复本:50,20,30
在此可以看出,修改了arrCopy的值,并不会改动到arrO,即arrCopy获得了一份 arrO的副本,与arrO已无关系了。
Demo3:Demo2这种拷贝叫“浅拷贝”,为什么呢?来看看Demo3就明白了。
//二维数组的实验,发现concat和slice方法行不通了
var arrO:Array=[[10,11,12],[20,21,22],[30,31,32]];
trace("源数组:"+arrO);
var arrCopy:Array=arrO.concat();
trace("复本:"+arrCopy);
arrCopy[0][0]=100;
trace("更改了arrCopy[0][0]后,源数组:"+arrO+",复本:"+arrCopy);
结果:
源数组:10,11,12,20,21,22,30,31,32
复本:10,11,12,20,21,22,30,31,32
更改了arrCopy[0][0]后,源数组:100,11,12,20,21,22,30,31,32,复本:100,11,12,20,21,22,30,31,32
从结果可以认看到,对arrCopy[0][0]的改动,都影响到arrCopy和arrO。这主要是因为:“在浅副本中,如果原始数组具有对象元素,则仅复制指向对象的引用而非对象本身。”
Demo4:那要如何解决Demo3碰到的问题呢?这就是接下来说的“深拷贝”。官方提供了一个方法,即将对象转换成字节对象,再进行拷贝,这样做,复本中就是一个完整的内容了,而不会只包含引用。这个方法在Java中很常见。
//二维数组的实验,使用深拷贝。
function clone(source:Object):* 
    var myBA:ByteArray = new ByteArray(); 
    myBA.writeObject(source); 
    myBA.position = 0; 
    return(myBA.readObject()); 
}
var arrO:Array=[[10,11,12],[20,21,22],[30,31,32]];
trace("源数组:"+arrO);
var arrCopy:Array=clone(arrO);
trace("复本:"+arrCopy);
arrCopy[0][0]=100;
trace("更改了arrCopy[0][0]后,源数组:"+arrO+",复本:"+arrCopy);
结果:
源数组:10,11,12,20,21,22,30,31,32
复本:10,11,12,20,21,22,30,31,32
更改了arrCopy[0][0]后,源数组:10,11,12,20,21,22,30,31,32,复本:100,11,12,20,21,22,30,31,32
由此可看出,对于多维数组,亦可实现完整的拷贝了。
小结:
@“深”/“浅”之说,关键之处在于拷贝的时候是拷贝的是引用还是数据本身。因为对于多维数组或是自定义的对象,变量名代表的大多是引用。
@在此虽是以二维数组来做“深浅”之说,但对于复杂类来说也是适用的。只是有一点需要注意,clone用的参数及返回的参数是Objet,这是所有类的父类。在实现的使用中,要以*来表示数据类型,不要进行强制转换。否则为出错。可看demo5:
Student.as
package 
{
    public class Student {
     private var _Name:String;
     public function set Name(s:String):void {
         this._Name = s;
     }
     public function get Name():String {
        return this._Name;
     }
    }
}
var s1:Student = new Student();
s1.Name="ttq";
var s2:Student = Student(clone(s1)); 
s2.Name = "ttq Copy";
trace("源对象=", s1.Name+",复本对象="+s2.Name);
由于进行了强制转换,会出现这个错误:
“TypeError: Error #1034: 强制转换类型失败:无法将 Object@2855d859 转换为 Student。
at arrTest_fla::MainTimeline/frame1()”
如果将var s2:Student = Student(clone(s1)); 改为:
var s2:* = clone(s1); 或是:var s2:Object = clone(s1); 
则没有错误,可得到预想的结果:
源对象= ttq,复本对象=ttq Copy

抱歉!评论已关闭.