浅拷贝就是成员数据之间的一一赋值:把值赋给一一赋给要拷贝的值。但是可能会有这样的情况:对象还包含资源,这里的资源可以值堆资源,或者一个文件。。当值拷贝的时候,两个对象就有用共同的资源,同时对资源可以访问,这样就会出问题。深拷贝就是用来解决这样的问题的,它把资源也赋值一次,使对象拥有不同的资源,但资源的内容是一样的。对于堆资源来说,就是在开辟一片堆内存,把原来的内容拷贝。 如果你拷贝的对象中引用了某个外部的内容(比如分配在堆上的数据),那么在拷贝这个对象的时候,让新旧两个对象指向同一个外部的内容,就是浅拷贝;如果在拷贝这个对象的时候为新对象制作了外部对象的独立拷贝,就是深拷贝 引用和指针的语义是相似的,引用是不可改变的指针,指针是可以改变的引用。其实都是实所谓“浅拷贝”,是指创建一个新的对象,其内容是原对象中元素的引用。(拷贝组合对象,不拷贝子对象)现了引用语义。 深拷贝和浅拷贝的区别是在对象状态中包含其它对象的引用的时候,当拷贝一个对象时,如果需要拷贝这个对象引用的对象,则是深拷贝,否则是浅拷贝。
objectcreate是深拷贝还是浅拷贝_object的copy方法
浅拷贝:也就是在对象时,只是对对象中的数据成员进行简单的赋值,如果对象中存在动态成员,即指针,浅拷贝就会出现问题。
深拷贝:对于深拷贝,针对成员变量存在指针的情况,不仅仅是简单的指针赋值,而是重新分配内存空间。
浅拷贝,即在定义一个类A,使用类似Aobj;Aobj1(obj);或者Aobj1=obj;时候,由于没有自定义拷贝构造函数,C++编译器自动会产生一个默认的拷贝构造函数。
这个默认的拷贝构造函数采用的是“位拷贝”(浅拷贝),而非“值拷贝”(深拷贝)的方式,如果类中含有指针变量,默认的拷贝构造函数必定出错。
用一句简单的话来说就是浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。
假如有一个成员变量的指针,charm_data;
其一,浅拷贝只是拷贝了指针,使得两个指针指向同一个地址,这样在对象块结束,调用函数析构的时,会造成同一份资源析构2次,即delete同一块内存2次,造成程序崩溃。
其二,浅拷贝使得obj.m_data和obj1.m_data指向同一块内存,任何一方的变动都会影响到另一方。
其三,在释放内存的时候,会造成obj1.m_data原有的内存没有被释放,造成内存泄露。
事实是这样的,当deleteobj.m_data,obj.m_data内存被释放后,由于之前obj.m_data和obj1.m_data指向的是同一个内存空间,obj1.m_data所指的空间不能在被利用了,deleteobj1.m_data也不会成功,一致已经无法作该空间,所以导致内存泄露。
深拷贝采用了在堆内存中申请新的空间来存储数据,这样每个可以避免指针悬挂。
可以看到在拷贝构造函数中为成员变量申请了新的内存空间,这就使得两个对象的成员变量不指向同一个内存空间,除非你的确需要这样做,用于实现一些其他的用途。
b
通俗解释:深拷贝是内容拷贝,浅拷贝是地址拷贝
console.log(array1); // ['a',1,true,{name:'zhang',age:18}]栗子中 array2是array1的浅拷贝对象,数组元素是原始数据类型的不会相互影响了(array1[0]),但是array1[3]是对象类型,还是会互相影响。区别点:
深拷贝会创建一个新的内存空间,拷贝的值是一样的,但是内存地址不一样。
浅拷贝只是拷贝指向原来对象的地址,使原对象的引用计数+1
像NSString、NSNumber这些不能包含其他对象的叫做非容器类对象
像NSArray、NSDictionary这些可以包含其他对象的叫容器类对象
通过对比不难发现:
上面我们使用的是不可变的NSString,下面我们再使用可变的NSMutableString对比一下:
不难发现,对于NSMutableString, 无论是copy还是mutableCopy都会创建一个新对象,属于深拷贝
不难发现,copy是浅拷贝,mutableCopy是深拷贝,不过需要注意的是容器对象的成员元素都指向相同的地址
对比可见,容器对象与非容器对象类似,可变对象的都是深拷贝,不可变对象copy是浅拷贝,mutableCopy是深拷贝
需要注意的是对容器而言,元素对象始终是指针
正如前面所说,容器对象中的元素对象无论是copy还是mutableCopy都是指针,如何实现容器对象的完全深拷贝呢?
浅拷贝又叫指针拷贝,只拷贝对象指针,不创建新的对象,拷贝对象和原对象都指向同一块内存地址的内容。
深拷贝又叫内容拷贝,深拷贝时系统会开辟新的内存空间,把对象存入新的内存区域,相较于原来对象,拷贝对象是一个新的对象,原对象的改变不影响新对象;
大家知道,我们创建NSString属性时候修饰词用的是copy,要是换成strong会怎么呢?请看:
可以看到我们修改strCoptTest,拷贝对象也被修改了。如果我们用copy修饰NSString属性则不会出现这问题,为什么copy修饰不会出现这个问题呢?
参照其他对象,在此猜测当copy对象为NSMutableString对象,NSCopying协议实现里新建了副本对象 (等于深拷贝,此时拷贝对象指向一个新地址)
对象用copy修饰
可以看到我们修改arrMut对象后,self.arrList对象的值被动被修改了,这不是我们想要的结果。
执行效果,可以看到避免了被动修改的情况
新建Person类
发现无论是strong还是copy修饰都不安全,和NSString例子同样情况,如下
上面说到是因为数组内对象没有实现深拷贝,所以我们的目的就是把数组内对象也实现深拷贝,正好NSArray也提供了对应方法:
1.不可变类型属性修饰词用copy;
2.对类对象,copy时内部成员变量也要深拷贝;
3.自定义的对象要使用copy的遵循NSCopying协议,实现copyWithZone方法;
1.不可变对象的copy都是浅拷贝,副本类型为不可变类型;
2.不可变对象mutableCopy是深拷贝,副本类型为可变类型;
3.可变对象的copy都是是深拷贝,副本类型为不可变类型;
1.copy修饰的属性,在set方进行copy作,而strong只是赋值;
2.浅拷贝会对原对象指针进行引用,原对象引4.可变对象mutableCopy是深拷贝,副本类型为可变类型;用计数retainCount会+1,相当于strong;
3.深拷贝等于重新创建一个新对象,不会对原对象有引用关系,原对象retainCount不变;
git传送门:CopyTestDemo
前提:原始数据类型和对象类型赋值时的异JaScript的数据类型分为原始数据类型和对象类型。二者在内存中存放的方式不同,导致了其赋值时异。分别举个栗子
public string PSexvar x = 1; var y = x; //y获得了和x同样的值
y = 2;
console.log(x); // 1
var m = [1,2]; //m存放的是指向[1,2]这个数组对象的引用地址
var n = m; //n也获得 [1,2]数组对象的引用地址
n[0] = 3;
console.log(m); //[3,2]由上栗子可以看出 :原始数据类型赋值时,给的是实实在在的数据值 ,赋值后二者只是值一样而已,不会相互影响; 而对象类型,给的是 原数据的引用地址,所以新旧数据会互相影响,因为本质上还是同一个数据对象,如上栗中的数组
什么是浅拷贝?顾名思义,浅拷贝就是流于表面的拷贝方式;当属性值为对象类型时,只拷贝了对象数据的引用,导致新旧数据没有完全分离,还会互相影响。再举个栗子··· //测试数据
var array1 = ['a',1,true,{name:'lei',age:18}];
//concat() slice() 实现浅拷贝
var array2 = array1.concat()
array2[0] = 'b'; //array1[0]是原始数据类型 所以是直接赋值的
array2[3].name = 'zhang'; //array1[3]是对象数据类型 所以拷贝的是对象的引用,其实还是和原数组使用同一对象
如何实现浅拷贝上栗中的 array.concat()或者array.slice() 是特殊的实现数组浅拷贝的方式。
如何自己实现呢?遍历对象/数组的每个属性,然后赋值给一个新的对象不就行了么,如下实现
function shallowCopy( target ){
if(typeof target !== 'object') return ;
//判断目标类型,来创建返回值
var newObj = target instanceof Array ? [] : {};
//只元素自身的属性,不原型链上的
newObj[item] = target[item]
}}
return newObj
}//测试
var copy = shallowCopy(test);
console.log(copy[2].name); //lei
copy[2].name = 'zhang';
console.log(test[2].name); //zhang 原数据也被修改深拷贝及其实现从浅拷贝解释基本可以明白,深拷贝就是 ‘完全’拷贝,拷贝之后新旧数据完全分离,不再共用对象类型的属性值,不会互相影响。
实现方式:
取巧方式 JSON.parse(JSON.stringify(Obj))
var copy1 = JSON.parse(JSON.stringify(test)); //特殊方式
console.log(copy1);
console.log(test); //[1,'a',{name:'lei',age:18}] 未受到影响注意:这种方式不能深拷贝有属性值为函数的对象, 可自行尝试
2. 实现深拷贝
已经实现了浅拷贝,思考下应该是对 对象类型属性值赋值时,导致的没有完全分离,所以要修改下 拷贝对象类型属性值的方式,对它再调用一次深拷贝,这样就实现了深拷贝,如下:
//实现深拷贝
function deepCopy( target ){
if(typeof target !== 'object') return ;
//判断目标类型,来创建返回值
var newObj = target instanceof Array ? [] : {};
//只元素自身的属性,不原型链上的
newObj[item] = typeof target[item] == 'object' ? deepCopy(target[item]) : target[item] //判断属性值类型 }
}return newObj
}//测试
var copy2 = deepCopy(test);
copy2[2].name = 'zhang'
console.log(test); ////[1,'a',{name:'lei',age:18}] 未受到影响 总结
一定要理解造成浅拷贝的原因:对象类型数据时,了引用地址,用的还是同一个数据对象;所以实现深拷贝的方式就是要对 对象类型属性值递归进行深拷贝,避免直接赋值。
“为学日益,为道日损,损之又损,以至于无为,无为而无不为”。用自己的思考去理解问题,从问题的源头探索,才有可能寻找到接近设计者思想的答案。网上有非常多的博客,博客内容良莠不齐,且绝大多数是为被大众证实,仅仅是一面之词。日常阅读,日常解惑,有这两篇文档可以阅读,分别是更新在MRR、ARC时代的两篇文章。
Updated: 2009-10-21
Memory Management Programming Guide for Core Foundation
Updated: 2012-07-17
Advanced Memory Management Programming Guide
下面只是笔者对上面两篇文章一点读后感。
引用拷贝 这个名词在文章中并没有直接指出,但有相关的概念提示,例如截图第二段中,第三行: copies the reference to the object ,第五行中提到: the reference is duplicated ,拷贝的结果是一个新的指针,故亦称之为 指针拷贝 。
下面以整数的拷贝为例:
myInt2 = myInt1
myCFString2 = myCFString1
上面提到了 CFStringCreateCopy ,这个方法仅仅是适用于字符串的copy,而对于array,同样有 CFArrayCreateCopy 方法。 CFxxtypexxCreateCopy 系列的方法都会拷贝一个新的对象。
例如CFStringRef、CFDataRef,在使用 CFxxtypexxCreateCopy 时,都会拷贝一个全新的对象。
从上面的一些特点不难分析出, 深拷贝是一种概念 ,而不是某个具体的作。不管是对象,还是自定义的实例对象,除了对象本身,他们还依赖一些其他对象,我们可以通过引用的方式来表示其他对象。当对象本身被拷贝时,仅仅只会开辟这个对象需要的空间,然后将原对象中的数据一比一一份存到这块内存,到这里就是浅拷贝的过程。如果是深拷贝,还需要把所有引用指向的对象再拷贝一份,让引用也指向新的对象,这样才能将这个对象以及这个对象所依赖的其他对象完全与原来的那份隔离开。
满足以下两个过程的拷贝可以称作深拷贝:
上面仅仅是对平时一直遇到,但没有理解透彻的概念做了一个整理,加深对这些概念的理解。上面的三个拷贝都是在MRR模式下, Core Foundation 框架中的概念,而且有API支持。在ARC模式下,我们遇到多的其实是 strong/copy/mutableCopy 这些关键词。
这张图可以佐证上面的一些概念。 Foundation 框架中的 copy 只表示一件事—— 拷贝对象 ,所以 copy 与 浅拷贝、深拷贝 的概念无直接联系。在ARC中的 copy 可以理解为MRR中的 CreateCopy —— The Create Rule
上述案例介绍了 create 和 copy 的规则,与上面分析的一致,而且完全没有提到任何关于 mutable 相关的解释。下面再看一个API的定义
CFArrayCreateMutableCopy
根据上面对 深拷贝概念 的对比,发现 mutableCopy 并没有完成深拷贝的过程,仅仅只是一个浅拷贝的过程。与 copy 不同的是,返回的容器对象是可变的,两者异仅此而已。对容器对象,有以下特点:
那么在 Foundation 框架中要完成深拷贝,可以使用框架提供的API:
initWithArray:copyItemsConsole.Read();:
Memory Management Programming Guide for Core Foundation
Advanced Memory Management Programming Guide
CFArrayCreateMutableCopy
initWithArray:copyItems:
浅拷贝就比如像引用类型,而深拷贝就比如值类型。
深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。举个例子,一个人名叫张三,后来用他克隆(假设法律允许)了另外一个人,叫李四,不管是张三缺胳膊少腿还是李四缺胳膊少腿都不会影响另外一个人。比较典型的就是Value(值)对象,如预定义类型Int32,Double,以及结构(struct),枚举(Enum)等。
C#中有两种类型变量,一种 是值类型变量,一种是引用类型变量。对于前者,copy是属于全盘;而对于后者,一般的copy只是浅copy,相当于只传递一个引用指针一样。因此 对于后者进行真正copy的时候,也是费事的,具体的说,必须为其实现ICloneable接口中提供的Clone方法。
浅拷贝(影子克隆):只对象的基本类型,对象类型,仍属于原来的引用.
深拷贝(深度克隆):不紧对象的基本类,同时也原对象中的对象.就是说完全是新对象产生的.
浅 拷贝和深拷贝之间的区别:浅拷贝是指将对象中的数值类型的字段拷贝到新的对象中,而对象中的引用型字段则指它的一个引用到目标对象。如果改变目标对象 中引用型字段的值他将反映在原是对象中,也就是说原始对象中对应的字段也会发生变化。深拷贝与浅拷贝不同的是对于引用的处理,深拷贝将会在新对象中创建一 个新的和原是对象中对应字段相同(内容相同)的字段,也就是说这个引用和原是对象的引用是不同的,我们在改变新对象中的这个字段的时候是不会影响到原始对 象中对应字段的内容。所以对于原型模式也有不同的两种处理方法:对象的浅拷贝和深拷贝。
MemberwiseClone 方法创建一个浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段到该新对象。如果字段是值类型的,则对该字段执行逐位。如果字段是引用 类型,则引用但不引用的对象;因此,原始对象及其复本引用同一对象。深拷贝,即实现ICloneable接口.ICloneable可用于深拷贝 和浅拷贝。
这些都是概念,但是需要我们理解,下面介绍实例:
using System.Text;
namespace WindowsApplication1
{public class ClassA:ICloneable
{public int Value = 0;
public object Clone()
{return this.MemberwiseClone();
}}
public class ClassB:ICloneable
{public ClassA //实现浅拷贝Member= new ClassA();
public object Clone()
{//浅
//return this.MemberwiseClone();
//深
ClassB obj= new ClassB();
//obj.Member= (ClassA)Member.Clone();
}}
public class class4
{public static void Main()
{ClassB = new ClassB();
.Member.Value = 15;
ClassB nb = (ClassB).Clone();
nb.Member.Value = 6;
Console.Write(.Member.Value.ToString() + "---" + nb.Member.Value.ToString());
ClassA nnb = new ClassA();
nnb.Value = 111;
ClassA bbn = new ClassA();
bbn = (ClassA)nnb.Clone();
bbn.Value = 222;
Console.Write(bbn.Value);
}}
}其中.MemberwiseClone()在上面已经介绍过了,通过实例可以清楚看到浅拷贝与深拷贝的区别
再来个:
using System.Text;
namespace WindowsApplication1
{class Program
{public class Sex
{set
{_PSex = value;
}get
}}
}public class Person : ICloneable
{private Sex _pSex = new Sex();
public int aa = 1213;
public string pSex
{set
{_pSex.PSex = value;
}get
{return _pSex.PSex;
}}
private string _PName;
public string PName
{set
{this._PName = value;
}get
{return this._PName;
}}
public void ShowPersonInfo()
{Console.WriteLine("-------------------------");
Console.WriteLine("Name:{0} Sex:{1}", _PName, this.pSex);
Console.WriteLine("-------------------------");
Console.WriteLine(this.aa);
}//浅拷贝
public object Clone()
{return this.MemberwiseClone();
}//深拷贝
public object DeepClone()
{Person newP = new Person();
newP.PName = this._PName;
return newP;
}}
static void Main(string[] args)
{Console.WriteLine("原对象:");
Person p = new Person();
p.PName = "JackLee";
p.pSex = "男";
p.ShowPersonInfo();
//浅拷贝
Person copy = (Person)p.Clone();
//深拷贝
Person dcopy = (Person)p.DeepClone();
Console.WriteLine("修改后的原对象:");
p.PName = "JackZhao";
p.pSex = "女";
p.ShowPersonInfo();
Console.WriteLine("修改后的浅拷贝对象:");
copy.ShowPersonInfo();
Console.WriteLine("修改后的深拷贝对象:");
dcopy.ShowPersonInfo();
Console.WriteLine("直接拷贝对象:");
Person PP = p;
PP.ShowPersonInfo();
Console.ReadLine();
}}
}接下来介绍一下数组的拷贝:
首先数组的直接拷贝也就是,不用多说看例子:
int [] numbersCopy = numbers;
numbersCopy[2] = 0;
Console.Write(numbers[2]);
Console.Write(numbersCopy[2]);
结果就是
道理很简单,数组的也就是引用传递,指向的是同一个地址,这不是我们介绍的重点
接下来
看一下概念
数组是引用类型,所以将一个数组变量赋予另一个数组变量,就会得到两个指向同一数组的变量。而数组,会使数组实现ICloneable接口。这个接口定义的Clone()方创建数组的浅副本。
如果数组的元素是值类型,就会所有的值,如果数组包含引用类型,则不元素,而只引用,
除了使用Clone()方法之外,还可以使用Array.Copy()方法创建浅副本。但Clone()方法和Copy()方法有一个重要区别:Clone()方创建一个新数组,而Copy()方法只是传送了阶数相同、有足够元素空间的已有数组。
提示:
如果需要包含引用类型的数组的深副本,就必须迭代数组,创建新对象。
看一下例子:
using System.Text;
namespace WindowsApplication1
{class Class2
{public static void Main()
{int[] numbers = { 2, 3, 4, 5 };
int[] numbersCopy = new int[5];
numbers.CopyTo(using System;numbersCopy,0);
numbersCopy[2] = 0;
int[] numbers1 = { 2, 3, 4, 5 };
int[] numbersClone1 = (int[])numbers1.Clone();
numbersClone1[2] = 0;
Console.Write(numbers[2] + "---" + numbersCopy[2]);
}}
}我这里介绍的主要是数组的clone和copyto的用法,两者都不会改变其中的值,与上面我们的有很大的区别
在JS中拷贝、一个对象的方式有多种,我常用的一般是 Object.assign({},sourceObj)。
//修改拷贝后的数据Object.assign()因为 Object.assign()拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。如果合并的对象是多层嵌套对象那就属于浅拷贝了,修改内层对象的值还是会影响原对象。
要想实现深度可以使用JSON方式。
写法如下:
它的原理是把JS对象转换为JSON字符串,再由JSON字符串转换为JS对象,这样新对象的指针就不会指向原对象的指针了。但这种也有副作用的,有一下几点副作用:
1、有属性包含时间对象,拷贝后就是字符串的形式。
2、有函数或undefined,拷贝后会丢失。
3、有RegExp和Error对象,拷贝后变为空对象。
4、存在循环引用的情况,没确拷贝。
6、属性值为NaN、Infinity,拷贝后变为null。
虽然有些副作用,但大多情况还是符合要求可以使用。
1、浅拷贝(shallow copy)
常见的浅拷贝有:切片作、工厂函数、对象的copy()方法、copy模块中的copy函数。
2、深拷贝(de比如ep copy)
所谓“深拷贝”,是指创建一个新的对象,然后递归的拷贝原对象所包含的子对象。深拷贝出来的对象与原对象没有任何关联。
copy浅拷贝,没有拷贝子对象,所以原始数据改变,子对象会改变
深拷贝,包含对象里面的自对象的拷贝,所以原始对象的改变不会造成深拷贝里任何子元素的改变
object.assign():用于将源对象(source)中可枚举的属性到目标属性(target)中,并返回目标对象。
console.log(Objeusing System.Collections.Generic;ct.assign(a,b,c));
//{ a: 1, b: 2, c: 3 }
浅拷贝:也叫引用拷贝,公用一块内存地址,一个改变另一个也改变;
深拷贝:创建新的内存地址保存值,与原对象完全独立。
深拷贝和浅拷贝只针对复杂的复杂的对象有别;
注意:针对object.assign()而言,如果属性值是简单类型(number,string),通过object.assign({},src)得到的新对象是深拷贝对象;如果属性值为对象或其他引用类型,得到的新对象为浅拷贝对象。
浅拷贝就是成员数据之间的一一赋值:把值赋给一一赋给要拷贝的值。但是可能会有这样的情况:对象还包含资源,这里的资源可以值堆资源,或者一个文件。。当值拷贝的时候,两个对象就有用共同的资源,同时对资源可以访问,这样就会出问题。深拷贝就是用来解决这样的问题的,它把资源也赋值一次,使对象拥有不同的资源,但资源的内容是一样的。对于堆资源来说,就是在开辟一片堆内存,把原来的内容拷贝。
如果你拷贝的对象中引用了某个外部的内容(比如分配在堆上的数据),那么在拷贝这个对象的时候,让新旧两个对象指向同一个外部的内容,就是浅拷贝;如果在拷贝这个对象的时候系统为我们实现容器对象的完全深拷贝提供了方法为新对象制作了外部对象的独立拷贝,就是深{return _PSex;拷贝
引用和指针的语义是相似的,引用是不可改变的指针,指针是可以改变的引用。其实都是实现了引用语义。
深拷贝和浅拷贝的区别是在对象状态中包含其它对象的引用的时候,当拷贝一个对象时,如果需要拷贝这个对象引用的对象,则是深拷贝,否则是浅拷贝。
版权声明:本文内容由互联。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发 a13828211729@163.com 邮箱删除。