×

深拷贝和浅拷贝如何实现

深拷贝和浅拷贝如何实现(深拷贝、浅拷贝的理解与使用场景)

admin admin 发表于2024-01-06 17:38:25 浏览32 评论0

抢沙发发表评论

“深拷贝和浅拷贝如何实现”相关信息最新大全有哪些,这是大家都非常关心的,接下来就一起看看深拷贝和浅拷贝如何实现(深拷贝、浅拷贝的理解与使用场景)!

本文目录

深拷贝、浅拷贝的理解与使用场景

通俗解释:深拷贝是内容拷贝,浅拷贝是地址拷贝 区别点: 深拷贝会创建一个新的内存空间,拷贝的值是一样的,但是内存地址不一样。 浅拷贝只是拷贝指向原来对象的地址,使原对象的引用计数+1 像NSString、NSNumber这些不能包含其他对象的叫做非容器类对象 像NSArray、NSDictionary这些可以包含其他对象的叫容器类对象 打印结果如下: 通过对比不难发现: 上面我们使用的是不可变的NSString,下面我们再使用可变的NSMutableString对比一下: 打印结果如下: 不难发现,对于NSMutableString, 无论是copy还是mutableCopy都会创建一个新对象,属于深拷贝 打印结果如下: 不难发现,copy是浅拷贝,mutableCopy是深拷贝,不过需要注意的是容器对象的成员元素都指向相同的地址 打印结果如下: 对比可见,容器对象与非容器对象类似,可变对象的复制都是深拷贝,不可变对象copy是浅拷贝,mutableCopy是深拷贝 需要注意的是对容器而言,元素对象始终是指针复制 正如前面所说,容器对象中的元素对象无论是copy还是mutableCopy都是指针复制,如何实现容器对象的完全深拷贝呢? 系统为我们实现容器对象的完全深拷贝提供了方法

iOS深拷贝与浅拷贝(附demo)

浅拷贝又叫指针拷贝,只拷贝对象指针,不创建新的对象,拷贝对象和原对象都指向同一块内存地址的内容。

深拷贝又叫内容拷贝,深拷贝时系统会开辟新的内存空间,把对象存入新的内存区域,相较于原来对象,拷贝对象是一个新的对象,原对象的改变不影响新对象;

大家知道,我们创建NSString属性时候修饰词用的是copy,要是换成strong会怎么呢?请看:

可以看到我们修改strCoptTest,拷贝对象也被修改了。如果我们用copy修饰NSString属性则不会出现这问题,为什么copy修饰不会出现这个问题呢?

参照其他对象,在此猜测当copy对象为NSMutableString对象,NSCopying协议实现里新建了副本对象 (等于深拷贝,此时拷贝对象指向一个新地址)

对象用copy修饰

可以看到我们修改arrMut对象后,self.arrList对象的值被动被修改了,这不是我们想要的结果。

执行效果,可以看到避免了被动修改的情况

新建Person类

发现无论是strong还是copy修饰都不安全,和NSString例子同样情况,如下

可以看到arrMut的第一个成员变量person1的name被修改为haha,self.arrList的第一个成员的名字也变为了haha,这不是我们想要的,为什么呢?

上面说到是因为数组内对象没有实现深拷贝,所以我们的目的就是把数组内对象也实现深拷贝,正好NSArray也提供了对应方法:

1.不可变类型属性修饰词用copy; 2.对集合类对象,copy时内部成员变量也要深拷贝; 3.自定义的对象要使用copy的遵循NSCopying协议,实现copyWithZone方法;

1.不可变对象的copy都是浅拷贝,副本类型为不可变类型; 2.不可变对象mutableCopy是深拷贝,副本类型为可变类型; 3.可变对象的copy都是是深拷贝,副本类型为不可变类型; 4.可变对象mutableCopy是深拷贝,副本类型为可变类型;

1.copy修饰的属性,在set方法会进行copy操作,而strong只是赋值; 2.浅拷贝会对原对象指针进行引用,原对象引用计数retainCount会+1,相当于strong; 3.深拷贝等于重新创建一个新对象,不会对原对象有引用关系,原对象retainCount不变;

但是用strong修饰是浅拷贝,会存在原对象被修改,当前对象也被动被改的情况,所以对可变对象赋值时候手动调用mutableCopy;

git传送门:CopyTestDemo

Python中的深拷贝和浅拷贝

这篇文章主要介绍了Python中的深拷贝和浅拷贝详解,本文讲解了变量-对象-引用、可变对象-不可变对象、拷贝等内容,需要的朋友可以参考下要说清楚Python中的深浅拷贝,需要搞清楚下面一系列概念:变量-引用-对象(可变对象,不可变对象)-切片-拷贝(浅拷贝,深拷贝)【变量-对象-引用】在Python中一切都是对象,比如说:3, 3.14, Hello, ,{a:1}......甚至连type其本身都是对象,type对象Python中变量与C/C++/Java中不同,它是指对象的引用,Python是动态类型,程序运行时候,会根据对象的类型来确认变量到底是什么类型。单独赋值: 比如说:复制代码 代码如下: a = 3在运行a=3后,变量a变成了对象3的一个引用。在内部,变量事实上是到对象内存空间的一个指针因为Python的变量不过是对象的引用,或指向对象的指针,因此在程序中可以经常改变变量引用复制代码 代码如下: x = 42 #变量绑定到整型对象 x = Hello #现在又成了字符串 x = #现在又成了列表专业表述如下:变量是一个系统表的元素,拥有指向对象的连接的空间对象是被分配的一块内存,存储其所代表的值引用是自动形成的从变量到对象的指针特别注意: 类型属于对象,不是变量比如像刚才的a=3, 整数对象3包含了两重信息1.值为32.一个头部信息:告诉Pthyon,这是个整数对象共享引用: 比如说:复制代码 代码如下: a = 3 b = a在运行赋值语句b = a之后,变量a和变量b指向了同一个对象的内存空间.从上图可以看到,a和b,其id完全一样,指向同一个整数对象3,或者说同一块内存如果删掉a后, 不会影响b拷贝概念的引入就是针对:可变对象的共享引用潜在的副作用而提出的.【可变对象-不可变对象】在Python中不可变对象指:一旦创建就不可修改的对象,包括字符串,元祖,数字在Python中可变对象是指:可以修改的对象,包括:列表、字典上面说的a,b都是整数,整数是不可变对象,如果是可变对象的话,就是另外一回事了。复制代码 代码如下: L1 = #L1变量指向的是一个可变对象:列表 L2 = L1 #将L1值赋给L2后,两者共享引用同一个列表对象 L1 = 200 #因为列表可变,改变L1中第一个元素的值 L1; L2 #改变后,L1,L2同时改变,因为对象本身值变了如果不想改变列表L2的值,有两种方法:切片 和 copy模块复制代码 代码如下: L1 = L2 = L1 id(L1);id(L2) #共享引用一个可变对象45811784L45811784L L2 = L1 #切片操作 id(L1);id(L2) #切片后,对象就不一样了45811784L45806920L L1 = 200 L1;L2 #L1发生改变,L2没有变化【拷贝】1. 切片技术应用于所有的序列,包括:列表、字符串、元祖但切片不能应用于字典。对字典只能使用D.copy()方法或D.deepcopy()方法.2. 深浅拷贝,即可用于序列,也可用于字典复制代码 代码如下: import copy X = copy.copy(Y) #浅拷贝:只拷贝顶级的对象,或者说:父级对象 X = copy.deepcopy(Y) #深拷贝:拷贝所有对象,顶级对象及其嵌套对象。或者说:父级对象及其子对象如果字典只有顶级对象:如果字典中嵌套对象:【结论】深浅拷贝都是对源对象的复制,占用不同的内存空间如果源对象只有一级目录的话,源做任何改动,不影响深浅拷贝对象如果源对象不止一级目录的话,源做任何改动,都要影响浅拷贝,但不影响深拷贝序列对象的切片其实是浅拷贝,即只拷贝顶级的对象

什么是浅拷贝和深拷贝

浅拷贝

MyClass a,b;a=b;

为了封装性和解耦,同类型的两个对象之间进行赋值操作时,所有成员变量被复制,包括私有成员、指针变量。

 

类的成员函数在传递或返回对象时都会进行对象复制产生临时对象,比如函数调用时实参变为形参,以及函数返回对象。考虑到性能和用户要求不同,编译器不复制对象内部动态创建的内存块,比如用new和malloc申请的内存,一般为数组或读取的文件数据流。

 

如果类中只有int、double、char、bool这类基本类型的变量,由于基本变量的所占空间是已知的,所以在编译时编译器就可确定所需内存大小而进行分配(静态分配内存),对象a和对象b他们的变量存在于各自的内存块中(对象内存空间),浅拷贝后a的所有变量都被拷贝,如果a中的变量值被修改不会影响b内的变量值。

 

但假如这个类是个含有一个指针变量p,其中b对象在运行时分配了块内存(比如动态创建了数组,或读取了文件数据流,即动态分配内存),p指向该内存,浅拷贝后,a的p被赋值,因此同样指向该内存块,也就是说a的指针p指向了b的指针p指向的内存。这就存在一个问题,两个对象的指针都指向一块内存,如果通过a的指针p修改数据,显然就会使得b内的数据也受到影响,这不符合解耦原则而破坏了对象的封装性。

 

深拷贝

由于上述原因,C++提供了拷贝构造函数接口以及赋值操作符重载函数接口,以便开发者处理动态分配的内存的复制工作,这类复制工作就叫深拷贝。

class MyClass{public:  MyClass(const MyClass & obj);//拷贝构造函数  MyClass & operator =(const MyClass & obj);//赋值操作符重载函数}

浅拷贝是系统自动调用的默认赋值操作符重载函数实现,而深拷贝需要开发者实现。

什么是深拷贝和浅拷贝以及如何实现深拷贝

简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果A没变,那就是深拷贝,自食其力。

此篇文章中也会简单阐述到栈堆,基本数据类型与引用数据类型,因为这些概念能更好的让你理解深拷贝与浅拷贝。

我们来举个浅拷贝例子:

let a=,b=a;console.log(a===b);a=1;console.log(a,b);

嗯?明明b复制了a,为啥修改数组a,数组b也跟着变了,这里我不禁陷入了沉思。

那么这里,就得引入基本数据类型与引用数据类型的概念了。

面试常问,基本数据类型有哪些,number,string,boolean,null,undefined五类。

引用数据类型(Object类)有Object,Array,Date等。

而这两类数据存储分别是这样的:

a.基本类型--名值存储在栈内存中,例如let a=1;

当你b=a复制时,栈内存会新开辟一个内存,例如这样:

所以当你此时修改a=2,对b并不会造成影响,因为此时的b已自食其力,翅膀硬了,不受a的影响了。当然,let a=1,b=a;虽然b不受a影响,但这也算不上深拷贝,因为深拷贝本身只针对较为复杂的object类型数据。

b.引用数据类型--名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值,我们以上面浅拷贝的例子画个图:

当b=a进行拷贝时,其实复制的是a的引用地址,而并非堆里面的值。

而当我们a=1时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝了。

那,要是在堆内存中也开辟一个新的内存专门为b存放值,就像基本类型那样,起步就达到深拷贝的效果了

以上就是我们为大家找到的有关“深拷贝和浅拷贝如何实现(深拷贝、浅拷贝的理解与使用场景)”的所有内容了,希望可以帮助到你。如果对我们网站的其他内容感兴趣请持续关注本站。