如何在MATLAB类中获取静态成员变量?

16

有没有办法在 MATLAB 类中定义静态成员变量?

以下代码无法实现此功能:

classdef A

    properties ( Static )
        m = 0;
    end
end

建议使用关键字“Constant”而不是“Static”,常量属性是不能被修改的。我想要一个对类A的所有对象都通用的变量,并且我希望能够在类A的方法中修改该变量。

因此,我需要一个私有静态成员变量。是否有一种方法在MATLAB中获得它?


发现可以通过在静态成员函数中使用持久化变量的方法来实现这一点。

在这种情况下,您应该从以下基类继承所有类。

classdef object < handle

    properties ( GetAccess = 'public', SetAccess = 'private' )
        id
    end

    methods ( Access = 'protected' )
        function obj = object()
            obj.id = object.increment();
        end
    end

    methods ( Static, Access = 'private' )
        function result = increment()
            persistent stamp;
            if isempty( stamp )
                stamp = 0;
            end
            stamp = stamp + uint32(1);
            result = stamp;
        end
    end  
end

1
据我所知,它不是为此而设计的,但persistent可以工作吗? - Tobias Kienzler
1
是的,我在静态成员函数中使用持久变量找到了一个解决方法。 - Vahagn
1
您应该将其作为答案发布,以供其他对此问题感兴趣的人参考。 - Tobias Kienzler
“persistent” 不会为子类单独分配。 - Eric
4个回答

18

这是一个设计问题,不能这样做。你应该使用一个persistent变量(来自于1980年的MATLAB技巧,在2011年得到应用)!

为了完整起见,我应该提到,实际上从2010b版本开始就有一个未经文档化、可能不再受支持的static属性修饰符。

关于背景信息,请参见此处 Dave Foti(MATLAB OO团队经理)的答案和博客

在MATLAB中,类可以定义常量属性,但不像C++等其他语言那样定义“静态”属性。有一些测试版曾经尝试过使用“静态”属性,然而该未经文档化的属性仍然保留下来。但是,该静态属性未经文档化,不应使用,并且在将来的MATLAB版本中可能会被删除。R2008a将其实现为Constant的同义词,并且除了Constant属性的文档行为外,没有提供额外的功能。

常量属性在声明时不能更改其初始值。MATLAB之所以按照这种方式工作,有几个原因。首先,MATLAB有长期规定,变量始终优先于函数和类的名称,并且赋值语句会在不存在变量的情况下引入一个变量。因此,任何形如“A.B=C”的表达式都将引入一个新变量A,该变量是包含字段B且值为C的结构数组。如果“A.B=C”可以指代类A的静态属性,则类A将优先于变量A,并且这将是与MATLAB之前版本不兼容的重大问题。这意味着包含赋值语句的m文件需要进行很大的更改。

语句"A.B = C"的含义可能会因在MATLAB路径中某处引入名为A的类而改变。MATLAB程序员一直能够依赖于赋值语句,引入与同名变量相同的影子变量。

其次,我们观察到静态数据很少在其他类中使用,除了作为类内私有数据或公共常量。例如,在几个Java类库的调查中发现,所有公共静态字段也都是final的。在MATLAB中,常数属性可以像Java中的"public final static"字段一样使用。对于类内部的数据,MATLAB已经拥有可在类内创建的持久变量,这些变量可以在类内部的私有或受保护方法或本地函数中私下使用。在MATLAB中,尽可能避免使用静态数据也有很好的理由。如果一个类具有静态数据,那么在多个应用程序中使用同一类可能会很困难,因为静态数据可能成为应用程序之间冲突的源头。在一些其他语言中,这不是太大的问题,因为不同的应用程序编译成运行在不同进程中的可执行文件,具有类静态数据的不同副本。在MATLAB中,许多不同的应用程序可能在同一个进程和环境中运行,并且每个类只有一个副本。

1
很遗憾,“here”链接已经失效了 :( - Rick Moritz

11

以下是在Matlab中创建静态属性的直接方法。与假设的(但不可能实现的;请参见Mikhail的答案)真正静态属性的唯一区别在于设置成员变量的语法。

classdef StaticVarClass
    methods (Static = true)
        function val = staticVar(newval)
            persistent currentval;
            if nargin >= 1
                currentval = newval;
            end
            val = currentval;
        end
    end
end

现在可以通过以下方式读取静态属性staticVar:

StaticVarClass.staticVar

...并且可以通过以下方式进行设置:

StaticVarClass.staticVar(newval);

例如,这是测试此功能时的预期输出:

>> StaticVarClass.staticVar
  ans =
      []
>> StaticVarClass.staticVar('foobar')
  ans =
      foobar
>> StaticVarClass.staticVar
  ans =
      foobar
>> 

这种方法同样适用于私有静态属性,就像你要求的那样,但演示代码会稍微长一些。请注意,这不是一个句柄类(虽然它在句柄类上也可以完美地工作)。
classdef StaticVarClass
    methods (Access = private, Static = true)
        function val = staticVar(newval)
            persistent currentval;
            if nargin >= 1
                currentval = newval;
            end
            val = currentval;
        end
    end

    methods
        function this = setStatic(this, newval)
            StaticVarClass.staticVar(newval);
        end

        function v = getStatic(this)
            v = StaticVarClass.staticVar;
        end
    end
end

...和测试:

>> x = StaticVarClass
  x = 
      StaticVarClass with no properties.
      Methods
>> x.getStatic
  ans =
      []
>> x.setStatic('foobar')
  ans = 
      StaticVarClass with no properties.
      Methods
>> x.getStatic
  ans =
      foobar
>> 

1

(仅供参考)在Matlab中,有另一种创建类似静态数据的方法。

假设您有一个名为“car”的“handle”类,如果您想让car类具有静态数据,您可以构造另一个句柄类,并通过组合在car类中使用它,后者作为car类的静态数据。

classdef car<handle 
    properties 
         static_data:STATIC_DATA_HOLDER;
    end
end

classdef STATIC_DATA_HOLDER<handle
    properties
        data
    end
end

这样,当您创建汽车类的第一个实例时,将创建 STATIC_DATA_HOLDER 的实例,并且当您创建汽车类的第二个实例时,它将使用先前创建的 STATIC_DATA_HOLDER 类。
这些代码已在 "MATLAB 2013b" 中进行了测试。

1
谢谢,作为补充提示,请看这里:https://www.mathworks.com/help/matlab/matlab_oop/static-data.html - Seyfi

0

另一个获取类似静态属性的解决方法是利用成员变量初始化代码仅在加载类文件时执行一次的事实。这意味着,如果您有像下面这样的定义:

classdef foo
    properties
        stuff = some_function()
    end
end

然后只调用some_function一次,如果它返回一个类类型的对象,则该对象将被所有实例共享。我添加了一个示例实现,展示了如何使用它:

classdef ClassWithStaticMembers
    properties
        classvars = StaticVarContainer('foo', 0, 'bar', 2);
        othervar
    end
    methods
        function obj=ClassWithStaticMembers(var)
            obj.othervar = var;
        end
    end 
end

classdef StaticVarContainer < dynamicprops
    methods
        function obj=StaticVarContainer(varargin)
            for i=1:2:numel(varargin)
                obj.addprop(varargin{i});
                obj.(varargin{i}) = varargin{i+1};
            end
        end
    end
end

如果您运行此示例代码
obj1 = ClassWithStaticMembers(3);
obj2 = ClassWithStaticMembers(5);
obj1.classvars.foo = [2,3];

obj1.othervar
obj1.classvars

obj2.othervar
obj2.classvars

你会发现,classvars确实是共享的。我认为这个解决方案比在函数中使用持久变量要好得多,因为你可以随意重用StaticVarContainer,它更容易使用,而且你可以直接在属性部分看到静态变量的初始化。

为了得到OP问题中所需的结果(即实现对象计数器),可以将共享属性设置为Constant,这样就可以在没有实例的情况下引用它:

classdef ClassWithCounter
    properties (Constant)
        static = StaticVarContainer('counter', 0);
    end
    methods
        function obj=ClassWithCounter()
            obj.static.counter = obj.static.counter + 1;
        end
    end 
end

clear all
obj1 = ClassWithCounter();
obj2 = ClassWithCounter();
obj3 = ClassWithCounter();

ClassWithCounter.static.counter

请注意,Constant 属性仅表示例如 obj1.static 不能被更改,但它不影响非常量的 obj1.static.counter,并且可以根据心愿进行设置。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接