浅拷贝如何适用于 Smalltalk 中的 Array?



因此,据我所知,当您将消息copy传递到数组时,会执行浅拷贝。两个对象应该指向同一个数组,理论上,当你改变一个时,你就会改变另一个。但是,当我运行此代码时,它就像一个深层副本。每个数组彼此独立。我在第一个数组中进行更改,第二个数组不受影响。我在第二个数组中进行更改,第一个数组不受影响。那么当我使用copy时,复制实际上是如何工作的呢?

| a b |
a := #('first' 'second' 'third').
b := a copy.
Transcript show: a = b;cr.
Transcript show: a == b;cr.
Transcript show: (a at: 1) == (b at: 1);cr.
b at: 1 put: 'newFirst'.
Transcript show: a;cr.
Transcript show: b;cr.
Transcript show: (a at: 1) == (b at: 1);cr.
a at: 2 put: '2nd'.
Transcript show: a;cr.
Transcript show: b;cr.
a := nil.
Transcript show: a;cr.
Transcript show: b;cr.

结果是:

true
false
true
#('first' 'second' 'third')
#('newFirst' 'second' 'third')
false
#('first' '2nd' 'third')
#('newFirst' 'second' 'third')
nil
#('newFirst' 'second' 'third')

复制操作确实会创建数组的浅表副本。这意味着将创建一个新数组,该数组引用与原始数组相同的对象("内容"(。

当您通过at:put:更改副本时,原始数组不受影响,因为它的引用不会更改。此外,更改副本的引用不会更改包含的对象本身。相反,副本现在只是引用其他对象(在您的情况下'newFirst''2nd'(。这就是为什么将新对象放入副本中不会影响原始数组内容的另一个原因。

但是,由于这是一个浅拷贝,因此不会复制原始数组中的字符串。您可以按如下方式观察:

a := Array with: (String newFrom: 'first') with: 'second' with: 'third'.
b := a copy.
(a at: 1) replaceFrom: 1 to: 5 with: '1st  '.  "in-place modification"
Transcript show: a = b; cr. "true"
Transcript show: (a at: 1); cr. "1st  "
Transcript show: (b at: 1); cr. "1st  "
Transcript show: (a at: 1) == (b at: 1); cr. "true"

在这里,数组的副本没有更改,但原始数组及其副本的公共元素之一被更改。如果复制操作很深,b at: 1不会受到第三行更改的影响。它将指向不同的字符串。


上面代码中的String newFrom:是为了避免修改字符串文本"first"。

因此,

据我了解,当您将消息副本传递给数组时,会执行浅层复制。两个对象应该指向同一个数组,理论上,当你改变一个时,你就会改变另一个。

这是你的错误。 诚然,复制会产生浅层副本,但你对浅层副本的理解是错误的。 浅拷贝将为您提供对象的新实例,但所有实例变量将继续指向相同的对象。 实际上,您只创建了一个新对象。

如果您希望变量"a"和"b"指向同一对象,您只需将它们相互分配:

b := a.

这将允许您修改"b",并使更改也影响"a"。

为了进一步扩展这个概念,你有deepCopy和deepDeepCopy等的概念。 这些变体背后的想法是,它们也为实例变量创建新对象。

从技术上讲,像 Array、OrderedCollection、String 等索引对象不使用实例变量,但概念是相同的 - deepCopy 复制复制的对象指向的对象。 deepDeepCopy将更深入地复制那些指向的对象。

看看这对你来说是否更有意义:

|a b|
a := #('first' 'second' 'third').
b := a copy.
(b at: 1) at: 1 put: $X.
a at: 2 put: '2nd'.
Transcript show: a; cr.
Transcript show: b; cr.

。生成以下输出:

#('Xirst' '2nd' 'third')
#('Xirst' 'second' 'third')

最新更新