本节书摘来自华章出版社《面向对象的思考过程(原书第4版)》一书中的第3章,第3.6节,[美] 马特·魏斯费尔德(Matt Weisfeld) 著黄博文 译更多章节内容可以访问云栖社区“华章计算机”公众号查看。
3.6 对象操作
当处理复杂的数据结构和对象时,编程中的很多最基本的操作会变得越来越复杂。例如,当你想复制或比较原始数据类型时,过程非常简单。然而,复制和比较对象则并不简单。《Effective C++》第34页中,Scott Meyers专门花了整个小节来讲述复制和分配对象。
类和引用
复杂的数据结构和对象的问题在于它们可能会包含引用。简单对引用的复制不能复制它引用的数据结构或对象。同样,当比较对象时,简单地比较两个指针只是比较了引用,而并未比较指针所指的对象。
当对对象进行比较和复制时你就会意识到该问题。具体来说,问题归结于你是否使用了指针。不管怎样,总有一种方式能复制对象。不过这种方式看上去简单,但实际并不简单。因为对象可以包含引用,而必须对整个引用树做有效的复制(如果你要创建一份深拷贝)。
深拷贝与浅拷贝
追踪所有的引用,并对所有引用对象都创建拷贝,这种方式称为深拷贝。深拷贝会拥有很多层级。一个对象引用了很多对象,而这些被引用的对象也可能引用其他对象。拷贝本身开销巨大。浅拷贝只会简单地拷贝引用,而不会深入层级。在《Java面向对象设计》一书第265页的“只见树木不见森林”一节中,Gilbert和McCarty对深拷贝和浅拷贝进行了精彩的讨论。
如图3-8所示,如果只是简单地拷贝对象(称为逐位复制),只会拷贝引用,而不会拷贝实际的对象。因此,两个对象(原始对象和副本)会引用(指向)同一个对象。为了完全复制(即需要复制每个引用对象),你必须编写代码来创建所有的子对象。
当比较对象时也有类似的问题。正如拷贝功能一样,比较功能并不像看起来那么简单。因为对象包含引用,必须根据引用树进行有效的对象比较。大多数情况下,语言提供了一种默认的机制来比较对象。当设计类时,你应当在类中提供一个比较功能,从而保证类的行为是预期的。