深拷贝与浅拷贝
之前在学c++的时候,接触到了这两个概念,不过当时也不能完全理解,然后这两天看《深度探索C++对象模型》中Copy Constructor那节的时候,了解到了memberwise和bitwise两种拷贝方式,其对应的就是深拷贝与浅拷贝,因此今天搜搜资料,总结一下笔记。
简单来说,两者的异同如下:
-
对于简单对象(int,char,或者不含指针成员的自定义对象)来说,深拷贝和浅拷贝的效果都是一样的,都是开辟一块新的内存空间,来存放副本。
-
而对于含有指针成员的自定义对象来说,浅拷贝和深拷贝之间就有不同了。若我们没有为该对象定义拷贝构造函数,那么编译器会构造一个默认拷贝构造函数,并在拷贝对象时调用该函数,此时进行的是浅拷贝,即直接拷贝指针内容(即一个地址值),而非拷贝指针所指的对象。此时就导致有两个指针指向同一内存区域,因此在析构的时候,会对该内存区域析构两次,会报错。而深拷贝就不一样了,深拷贝需要我们自己定义拷贝构造函数,然后开辟出一块新的内存空间来放置 拷贝方 的指针成员所指向的对象。如下:
Student::Student(const Student &s){ name = new char(20); // char *name memcpy(name, s.name, strlen(s.name)); }
此时,
name
和s.name
两个指针指向的是两个不同的内存地址。
在Python中,copy()
方法和deepcopy()
方法实现了浅拷贝和深拷贝。
针对Python中简单对象的复制,copy()
和deepcopy()
没有什么区别,就是和大家通常理解的复制是一样的,在内存中新开辟一个空间,将原来地址中的数据拷贝到新的地址空间中。说明一下:我们这里所说的简单对象可以理解为最常见的对象,不包含的子对象的对象,也就是包含普通元素(数字,字符串)的对象。
而对于复杂对象的复制中,复杂对象可以理解为另外包含其他简单对象的对象,也就是包含子对象的对象,例如:List中嵌套List,或者Dict中嵌套List等,深拷贝和浅拷贝就不同了。若使用.copy()
来拷贝的话,仅仅是拷贝了子对象的地址,而非对象本身,只有用深拷贝.deepcopy()
,才能真正地拷贝子对象,其的差异见如下这个图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HUIrwKK2-1648435456424)(C:\Users\PrinceAlLHH\AppData\Roaming\Typora\typora-user-images\image-20211209224950146.png)]
注意:
-
C++中,对于含有自定义成员对象的类时,是按照递归的方式进行一个memberwise拷贝。
-
在Python中,没有指针的定义,但有引用的定义,我们可以直接将 Python 中的一个 list 当作指针, 所以和C++的一样,当对象中含指针成员时,就不能用浅拷贝。
-
python中引用的含义:
list的引用,指的是列表的地址,并不是列表的内容。对于含有
list
的list
,或含有list
的dict
,都要使用deepcopy()
来拷贝,不然只会拷贝到内嵌list的引用dict的引用,指的是字典的地址,并不是字典的内容。对于还有
dict
的列表,或含有dict
的dict
,都要使用deepcopy()
来拷贝,不然只会拷贝到内嵌dict的引用,如下:import copy if __name__ == "__main__": a = dict(name='wyuheng', age=22) b = dict(hight='181') c = [a, b] d = copy.copy(c) print(c[0] is d[0]) d[0]["name"] = 'hezeli' print(c[0])
输出为:
True {'name': 'hezeli', 'age': 22}
可看出,拷贝出的d[0]和c[0]指向的是同一块内存,所以,改变d[0],也会改变c[0]