【问题标题】:MATLAB field access with inheritance modifying all instances?具有修改所有实例的继承的 MATLAB 字段访问?
【发布时间】:2015-08-19 09:18:14
【问题描述】:

我正在尝试使用 MATLAB OOP 功能为网格开发数据结构。长话短说,我正在修改一个实例的字段,该实例继承自与另一个实例相同的基类,并且两个实例都被修改,就好像该字段被声明为静态一样!

我在 MATLAB 的抽象基类 (m_element) 中有这段代码:

properties(Access = protected)
    nodes = containers.Map('KeyType','int64', 'ValueType', 'any');
    faces = containers.Map('KeyType','int64', 'ValueType', 'any');
end

这些字段表示每个元素的连接性。例如,哪些节点是第 n 个节点的邻居,或者哪些面与第 n 个节点相邻。

我还有另外两个类:m_face 和 m_node,每一个都继承自 m_element。 m_node 很简单:

classdef m_node < m_element
    properties
        x = 0;
        y = 0;
        z = 0;
    end

    methods
        function node = m_node(gmsh_id, x, y, z)
            node = node@m_element(gmsh_id);

            node.x = x;
            node.y = y;
            node.z = z;
        end
    end
end

但是当谈到 m_face 时,我遇到了一个问题。这是出现问题的构造函数:

function face = m_face(varargin)

    face = face@m_element(varargin{1});


    for k = 2:nargin
        nod = varargin{k};


        if(~isa(nod, 'm_node'))
            error('Algum dos argumentos não é um node!');
        elseif (~isvalid(nod))
            error('Algum dos argumentos não é válido!');
        else

            face.nodes(nod.gmsh_id) = nod;

            nod.faces(face.gmsh_id) = face;
        end

    end
end

m_face 构造函数期望人脸 ID 作为第一个参数,其余的应该是构成人脸的节点。 face.nodes(nod.gmsh_id) = nod; 似乎是我的问题所在。我有一个 m_mesh 类,它应该包含每个节点和面:

classdef m_mesh < handle
    properties(SetAccess = private)
        nodes = containers.Map('KeyType','int64', 'ValueType', 'any');
        faces = containers.Map('KeyType','int64', 'ValueType', 'any');
    end

    methods

        function theMesh = m_mesh(msh)
            for idx = 1:numel(msh.POS(:,1))
                n = msh.POS(idx,:);
                theMesh.nodes(idx) = m_node(idx, n(1), n(2), n(3));
            end

            for idx = 1:numel(msh.TRIANGLES(:,1))
                ele = msh.TRIANGLES(idx,:);
                nod(1) = theMesh.nodes(ele(1));
                nod(2) = theMesh.nodes(ele(2));
                nod(3) = theMesh.nodes(ele(3));

                theMesh.faces(idx) = m_face(idx, nod(1), nod(2), nod(3));
            end
        end
    end
end

构造函数的msh 参数保存节点空间位置,以及组成每个面的节点(在本例中为三角形)。

这是我构建网格时得到的结果:

>> mesh = m_mesh(m)

mesh = 

m_mesh with properties:

nodes: [5x1 containers.Map]
edges: [0x1 containers.Map]
faces: [4x1 containers.Map]

>> nod = mesh.nodes.values();
>> nod{1}.i_nodes

ans = 

[1x1 m_node]    [1x1 m_node]    [1x1 m_node]    [1x1 m_node]    [1x1 m_node]

i_nodes 返回实例映射值。现在,这怎么可能?如果我还没有设置,为什么我的第一个(以及所有其他!)节点有五个相邻节点?当我从随机实例访问该字段时,MATLAB 为什么要更改所有实例和所有子类的非静态字段?

【问题讨论】:

    标签: matlab oop inheritance


    【解决方案1】:

    您应该在构造函数中初始化 nodesfaces 属性,而不是作为默认属性值。

    那么这里发生了什么?首先要注意的是containers.Map 是一个handle 类。

    MATLAB 中的正态变量具有 行为:

    >> a = 1;
    >> b = a;
    >> a = 2;
    >> b
    b =
         1
    

    请注意,当您更改 a 时,b 并未更改 - 它是 a 的副本,ab 是具有 变量按值传递 行为。

    其他一些变量具有句柄行为:

    >> a = figure;
    >> b = a;
    >> get(a, 'Name')
    ans =
         ''
    >> get(b, 'Name')
    ans =
         ''
    >> set(a, 'Name', 'hello')
    >> get(b, 'Name')
    ans =
    hello
    

    请注意,当您更改 a 时,b 已更改 - 它是对 a 的引用,ab 是具有 pass 的 句柄 变量-by-reference 行为。

    containers.Map 变量是句柄变量。

    要注意的第二件事是属性默认值在第一次实例化类时评估一次。 (如果您使用clear classes 清除类定义,则在您下次实例化它时将再次对其进行评估)。每次创建对象时评估它们。

    所以发生的情况是,第一次创建对象时,类被实例化,containers.Map 被评估,之后的每个对象都将 same containers.Map 作为其属性.由于它是一个句柄变量,因此您对一个对象中的属性所做的更改会在其他对象的属性中引用。

    您不希望这样:相反,您应该初始化类构造函数中的值。然后每次构造对象时都会对它进行单独评估,并且每个对象都会得到一个单独的对象。

    这种行为(即,当您将变量作为属性默认值时)可能会令人困惑,但我认为这是正确的行为 - 它已记录在案,尽管我认为它可能会更清楚地标明。这是讨论该主题的article - 在 cmets 中,作者、我自己和负责 OO 语法和设计的 MathWorks 开发人员就该问题进行了辩论。

    【讨论】:

    • 老兄!谢谢!这让我发疯了。我肯定会阅读这篇文章。
    • 你看,我以前习惯用 C++、Java 和其他 OO 语言编程。所以这种行为并没有发生在我身上,即使我已经习惯了价值/处理二元性。现在我明白了。
    猜你喜欢
    • 2010-11-26
    • 2018-03-05
    • 2013-11-20
    • 1970-01-01
    • 1970-01-01
    • 2015-02-05
    • 1970-01-01
    • 1970-01-01
    • 2011-09-12
    相关资源
    最近更新 更多