Search code examples
matlaboperator-overloadingoverloadingoverload-resolutionmatlab-class

Is there a good way here to allow users to both retain access control for properties and overload indexing methods?(matlab 2020a)


Assuming a member within a class has private access properties, i.e., GetAccess=private, if we use a "." type reference for that member in the overloaded subsref method, then the access property of that member becomes invalid at that point.

--------------------- So, is there a good way here to allow users to both retain access control for properties and overload indexing methods?


Using the builtin function cannot resolve this issue; please alternate between the code at lines 42 and 43. The fundamental reason is that setting GetAccess=private cannot prevent access within the class itself. Therefore, the code at line 42 effectively provides a backdoor for accessing object, rendering the GetAccess restriction ineffective, and this is not caused by subsref. Even if you don't use subsref, if you construct an MPolynom object within the class and assign the data from object.coef to another property, for example, object.xxx=object.coef, and then assign object.xxx externally, it can bypass the GetAccess restriction. After all, it cannot prevent class methods from accessing any class members.

classdef  MPolynom
    properties (SetAccess=public,GetAccess=private)
        coef;    %多项式系数
    end
    methods
        function obj = MPolynom(c)   %构造函数
            if isa(c,'MPolynom')
                obj.coef = c.coef;
            else
                obj.coef = c(:)';
            end
        end
        function plot(obj)    %重载plot函数
            r = max(abs(roots(obj.coef)));
            x = (-1.1:0.01:1.1)*r;
            y = polyval(obj.coef,x);
            plot(x,y);
            xlabel('X')
            ylabel('Y','Rotation',0)
            grid on
        end
        function r = plus(obj1,obj2)
            if ~isa(obj1,'MPolynom')       %如果第一个参数不是类MPolynom对象
                obj1 = MPolynom(obj1);    %创建一个类MPolynom对象obj1
            end
            if ~isa(obj2,'MPolynom')       %如果第二个参数不是类MPolynom对象
                obj2 = MPolynom(obj2);    %创建一个类MPolynom对象obj2
            end
            k = length(obj2.coef) - length(obj1.coef);   %计算两个阵列的长度差
            %创建一个类MPolynom对象作为返回值
            r = MPolynom([zeros(1,k) obj1.coef]+[zeros(1,-k) obj2.coef]);
        end
        function b = subsref(a,s)     %下标索引引用的实现
            switch s(1).type
               case '()'          %圆括号类型的引用
                    ind = s.subs{:};
                    class(a.coef)
                    b = polyval(a.coef,ind);  %返回多项式的值
                case '.'         %“.”类型的引用
                    switch s(1).subs
                        case 'coef'
                            b=builtin('subsref',a,s);
%                             b = a.coef;
                        case 'plot'     %由于方法plot没有返回值,这里单独列出访问过程
                            a.plot;
                        otherwise        %其他带返回值方法的引用
                            if length(s)>1
                                %b=subsref(a.coef,s);
                                b = a.(s(1).subs)(s(2).subs{:});  %带输入参数的方法引用
                            else
                                b = a.(s.subs);  %不带输入参数的方法引用
                            end
                    end
                otherwise
                    error('Specify value for x as obj(x)')
            end
        end
        function y = polyval(obj,x)   %计算多项式对象obj在x处的值
            y = polyval(obj.coef,x);
        end
    end
end

Use the code on line 43 and comment out the code on line 42.

classdef  MPolynom
    properties (SetAccess=public,GetAccess=private)
        coef;    %多项式系数
    end
    methods
        function obj = MPolynom(c)   %构造函数
            if isa(c,'MPolynom')
                obj.coef = c.coef;
            else
                obj.coef = c(:)';
            end
        end
        function plot(obj)    %重载plot函数
            r = max(abs(roots(obj.coef)));
            x = (-1.1:0.01:1.1)*r;
            y = polyval(obj.coef,x);
            plot(x,y);
            xlabel('X')
            ylabel('Y','Rotation',0)
            grid on
        end
        function r = plus(obj1,obj2)
            if ~isa(obj1,'MPolynom')       %如果第一个参数不是类MPolynom对象
                obj1 = MPolynom(obj1);    %创建一个类MPolynom对象obj1
            end
            if ~isa(obj2,'MPolynom')       %如果第二个参数不是类MPolynom对象
                obj2 = MPolynom(obj2);    %创建一个类MPolynom对象obj2
            end
            k = length(obj2.coef) - length(obj1.coef);   %计算两个阵列的长度差
            %创建一个类MPolynom对象作为返回值
            r = MPolynom([zeros(1,k) obj1.coef]+[zeros(1,-k) obj2.coef]);
        end
        function b = subsref(a,s)     %下标索引引用的实现
            switch s(1).type
               case '()'          %圆括号类型的引用
                    ind = s.subs{:};
                    class(a.coef)
                    b = polyval(a.coef,ind);  %返回多项式的值
                case '.'         %“.”类型的引用
                    switch s(1).subs
                        case 'coef'
%                             b=builtin('subsref',a,s);
                            b = a.coef;
                        case 'plot'     %由于方法plot没有返回值,这里单独列出访问过程
                            a.plot;
                        otherwise        %其他带返回值方法的引用
                            if length(s)>1
                                %b=subsref(a.coef,s);
                                b = a.(s(1).subs)(s(2).subs{:});  %带输入参数的方法引用
                            else
                                b = a.(s.subs);  %不带输入参数的方法引用
                            end
                    end
                otherwise
                    error('Specify value for x as obj(x)')
            end
        end
        function y = polyval(obj,x)   %计算多项式对象obj在x处的值
            y = polyval(obj.coef,x);
        end
    end
end

Both of the above code snippets yield the same result when executing the following command. Therefore, the effective way to enforce the GetAccess restriction is to directly use error('GetAccess=private') at line 42.

p=MPolynom([1,3,2,5]);
p.coef

Solution

  • It's a real shame that you're stuck on R2020a, because in R2021b there was introduced a mixin helper matlab.mixin.indexing.RedefinesParen which is specifically designed to help you out in this case. From the reference page, here's the sort of thing you need to do:

    classdef MyClass < matlab.mixin.indexing.RedefinesParen
        properties (Access=public)
            Label
        end
        methods (Access=protected)
            function varargout = parenReference(obj, indexOp)
                % reference
            end
    
            function obj = parenAssign(obj,indexOp,varargin)
                % assignment
            end
    
            function n = parenListLength(obj,indexOp,ctx)
                % instead of numel overloads
            end
    
            function obj = parenDelete(obj,indexOp)
                % paren deletion
            end
        end
    end