在大多数情况下,这两种方法的行为相同,选择只是一种偏好,具体取决于您认为哪种方法更清晰/更易于维护等。
但它们并不相同,在某些情况下,如果您不了解这些差异,您会遇到麻烦。
本质区别在于,在构造函数中初始化的属性在每次构造对象时都会单独初始化,而在属性块中使用默认值初始化的属性会在第一次读取类定义时初始化一次。属性块中的默认值是 class 的默认值,而不是对象的默认值;并且(使用反射)即使尚未实例化该类的实例,您也可以查询该默认值。
在大多数情况下,这没有区别 - 但是当初始值是句柄对象或非确定函数的输出时,它会这样做。所以考虑以下两个类:
classdef A
properties
foo
end
methods
function obj = A
obj.foo = containers.Map;
end
end
end
classdef B
properties
foo = containers.Map
end
end
注意containers.Map 是一个句柄类。
在A 类中,每次创建A 的新实例时,都会为其foo 获得一个新的/不同的containers.Map。在B 中,B 的每个实例都得到相同 containers.Map 的foo,因为该属性仅初始化一次,即第一次读取类定义。因此,如果您为 B 类的任何对象修改 foo,则该更改将传播到 B 类的所有其他实例,如您所见:
>> a1 = A; a2 = A; a1.foo('greeting') = 'hello'; a2.foo('greeting') = 'bonjour';
>> a1.foo('greeting'), a2.foo('greeting')
ans =
'hello'
ans =
'bonjour'
>> b1 = B; b2 = B; b1.foo('greeting') = 'hello'; b2.foo('greeting') = 'bonjour';
>> b1.foo('greeting'), b2.foo('greeting')
ans =
'bonjour'
ans =
'bonjour'
>> % Note that b1.foo has changed as a result of setting b2.foo
关于将类处理为默认值的这一点经常让人绊倒;但该行为并非特定于处理类。例如,考虑以下变体:
classdef A
properties
foo
end
methods
function obj = A
obj.foo = datetime('now');
end
end
end
classdef B
properties
foo = datetime('now')
end
end
这里A 将存储每个对象的创建时间,而B 将存储所有对象的类首次初始化的时间,无论它们是何时创建的。
如果您发现这种行为令人困惑,请参阅 https://undocumentedmatlab.com/blog/handle-object-as-default-class-property-value,尤其是该文章下方的评论线程,以讨论该问题并解释 MATLAB 以这种方式设计的原因。
编辑:cmets 中关于clear 的行为及其与此问题的关系的很好的后续问题。
使用上述类的第二个实现(使用datetime),看看以下内容:
>> a = A; b = B; datestr(a.foo), datestr(b.foo)
ans =
'01-Sep-2018 18:59:30'
ans =
'01-Sep-2018 18:59:30'
>> clear variables
>> a = A; b = B; datestr(a.foo), datestr(b.foo)
ans =
'01-Sep-2018 18:59:48'
ans =
'01-Sep-2018 18:59:30'
>> clear classes
>> a = A; b = B; datestr(a.foo), datestr(b.foo)
ans =
'01-Sep-2018 18:59:57'
ans =
'01-Sep-2018 18:59:57'
所以我们首先创建一个A和一个B,并显示它们的foos,它们都同时显示。然后我们等一会儿,做clear variables,然后我们再做一次。请注意,A 中的foo 是新时间,B 中的foo 仍与以前相同。最后我们再等一会儿,我们做clear classes,然后我们再做一次。这次A和B都有新的时间了。
为什么?因为clear variables 只是从工作区中删除了对变量的引用。 B 的类定义没有被清除,所以当我们创建另一个 B 时,它仍然使用第一次读取类定义时的值。 clear classes,相比之下,也删除了类定义,所以当我们稍后构造一个新的B 时,它会得到那个时间,因为然后重新读取类定义。所有这些都与A 无关,因为foo 只是在构建时被赋予了一个值。
注意clear classes 清除所有类定义:您可以使用clear B 清除仅类B 的定义。