创建LabVIEW类



LabVIEW 2018帮助


版本日期:2018年3月
产品编号:371361R-0118
查看产品信息

下载帮助(仅限Windows)


LabVIEW 2015帮助
LabVIEW 2016帮助
LabVIEW 2017帮助
LabVIEW 2018帮助
LabVIEW 2019帮助

通过创建LabVIEW类,可在LabVIEW中创建用户定义的数据类型。LabVIEW类定义了对象相关的数据和可对数据执行的操作(即方法)。通过封装继承可创建模块化的代码,使代码更易修改而不影响应用程序中的其它代码。

在LabVIEW中,类的数据是私有的,即只有类的成员VI才能访问该数据。类的数据可在私有数据控件中定义。创建和保存LabVIEW类时,LabVIEW将创建一个类库文件(.lvclass),其中定义了新的数据类型。类库文件记录了私有数据控件和所有被创建的成员VI的信息,比如VI列表以及VI各自的不同属性。类库和项目库(.lvlib)相似。类库和项目库(.lvlib)相似。不同的是,类库定义了新的数据类型。

私有数据控件对应唯一的类库文件,其中为新的数据类型定义了一簇数据,该簇也是类连线上的数据。LabVIEW的私有数据控件并不保存在磁盘上,而是保存在类库文件中。由于在类库文件中保存私有数据,不符合类定义的私有数据一定不会被使用。

提示:  如需保存类库文件、类成员VI,以及类自定义默认探针,可在磁盘上创建一个和LabVIEW类同名的目录,将属于该类库的文件保存在该目录中。如同一目录中包含了多个属于不同类库的文件,那么在不同类库中添加相同名称的VI时将产生冲突。在开发过程中重写动态成员VI将产生命名冲突。

封装

每个LabVIEW类包括一个数据簇和用于读写该簇的方法。LabVIEW类的数据是私有的,对于不是该类成员的VI来说是隐藏的。如需访问类的私有数据,必须创建方法,即创建该类的成员VI,通过成员VI中的函数对私有数据执行操作。封装就是将数据和方法合并到一个类中,类中数据仅可由类的成员VI访问。通过封装可创建模块化代码,有利于方便地更新或修改代码而不影响应用程序中其它部分的代码。

类中的数据是私有的,但成员VI却可以按不同的程度公开。方法的设置访问范围选项可以有如下设置:

  • 公共-任何VI都可将该成员VI作为子VI调用。
  • 库内-只有同类中的VI、类的友元或类的友元库中的VI可以调用库内成员VI。在项目浏览器窗口中,库内成员VI图标中有一个深蓝色的钥匙符号。
  • 保护-仅该成员VI所在的类及其子类中的VI可以调用该成员VI。在项目浏览器窗口中,受保护的成员VI图标中有一个暗黄色的钥匙符号。
  • 私有-仅该成员VI所在类中的VI可以调用该成员VI。在项目浏览器窗口中,私有成员VI图标中有一个红色的钥匙符号。
  • 未指定-仅当选中一个文件夹时,显示该选项。文件夹的访问范围未指定时,其访问范围默认为公共。默认情况下,如未对类中的文件指定访问范围,则这些文件夹的访问范围为公共。
    注:  如指定文件夹的访问选项,则访问设置适用于文件夹下的所有文件,并将覆盖各个文件的原有设置。
注:  如将动态分配VI设置为库内,这些VI将无法运行。为友元创建一个静态分配的包装VI,调用该包装VI并将其设置为库内,以此向友元赋予访问受保护动态分配VI的访问权限。

分配库的友元

将一个VI分配为库的友元,即是给予该VI调用库内任何成员VI的权限。也可分配一个库作为库的友元。

友元关系不具有传递性。例如,如第一个库分配第二个库为友元,第二个库分配第三个库为友元,第三个库不能作为第一个库的友元。除非第一个库将第三个库作为友元,第三个库无法访问第一个库的VI。如访问权限在库内的库指定某个类为友元,该类的成员VI可访问库的VI,但是友元关系不延展至类的子孙类。

可创建一个在LabVIEW类之外的VI,将公共成员VI作为子VI在程序框图上使用。公共成员VI允许用户操作类的私有数据。用户可在成员VI的程序框图上使用私有和受保护的成员VI操作LabVIEW用户不可见的类私有数据。独立于类的VI可作为类的友元,友元VI可调用库内的成员。对类的入口点进行限制,可减少对数据引入错误的机会,更便于调试代码。

关于LabVIEW类的封装和访问范围,可参考下列项目范例。

  • labview\examples\Object-Oriented Programming\Data Encapsulation\Data Encapsulation with LabVIEW Classes.lvproj

  • labview\examples\Object-Oriented Programming\Access Scope\Access Scope with LabVIEW Classes.lvproj

定义私有数据控件

创建LabVIEW类时,LabVIEW将自动创建类的私有数据控件。请注意在下列项目浏览器窗口中,LabVIEW类的图标是一个有色立方体。该立方体用于代表一个LabVIEW类。私有数据控件的图标是一个带有绿色圆柱体的有色立方体。圆柱体用于代表数据存储。同时,私有数据控件的图标中有一个红色钥匙符号,表示该控件是私有的。

通过控件编辑器窗口可对类的私有数据控件进行自定义。在项目浏览器窗口中双击类的私有数据控件,即可打开“控件编辑器”窗口。可将类私有数据的簇中的输入控件和显示控件放置到LabVIEW类的定义私有数据控件中。为类私有数据簇中的输入控件设置的默认值为类的默认值。

注:  私有数据控件不能包含XControl

以下范例中,汽车类的数据类型包含齿轮数量车门数量颜色三个数值,以及制造型号两个字符串。

注:  如类中不需要任何私有数据,类私有数据的簇中可以不设定任何数据。

用户可创建在前面板或程序框图上代表类的图标。单击类属性对话框常规设置页的编辑按钮,打开图标编辑器对话框。创建类图标后,LabVIEW将把类图标应用于类的所有对象修改类中各个对象的图标。

创建成员VI

通过创建成员VI(或称创建方法),可对类的私有数据执行操作。成员VI是LabVIEW类的方法。在LabVIEW类中创建,并在项目浏览器窗口中该类的私有数据控件下出现的VI,是该类的成员VI。

LabVIEW将类的数据定义为一个簇。所有成员VI都可对类数据的簇进行读写。LabVIEW为创建用于访问簇中各元素的VI提供了捷径。这种访问器VI是LabVIEW类的成员,可对类数据进行读写操作。如创建一个访问器VI以读取类数据,LabVIEW将取消对类数据的绑定,如下图所示。

如创建用于写入类数据的访问器VI,LabVIEW将把新值绑定至类数据,如下图所示。

也可使用解除捆绑按名称解除捆绑函数,在成员VI的程序框图中对类的私有数据解除捆绑。使用捆绑按名称捆绑函数可在访问和操作私有数据之后,将数据重新捆绑成簇。由于类的数据是私有的,若试图在非该类成员VI的程序框图中通过“捆绑”和“解除捆绑”节点访问该类数据,节点将自动断开无法运行。

注:  建议尽量使用“按名称捆绑”和“按名称解除捆绑”函数替代“捆绑”和“解除捆绑”函数,以免在私有数据的簇中插入新元素时VI断开。

如写入成员VI的操作将取消捆绑某个值,修改该值然后将值捆绑至对象,可使用元素同址操作结构,在结构两边放置解除捆绑和捆绑函数,以实现更高的效率。该结构可保证LabVIEW使用了某些内存优化技术。使用常规取消捆绑和捆绑节点时也可使用该内存优化的方法。但是,在复杂VI的情况下,LabVIEW编译器可能会认为优化不够安全而拒绝使用优化算法,导致运行速度变慢。元素同址操作结构保证了这些优化算法的安全性,确保VI按优化算法运行。

可通过各种方式创建成员VI。右键单击类并在以下快捷菜单项中选择:

  • 新建»VI-打开一个空的成员VI。
  • 新建»属性定义文件夹-创建一个属性定义文件夹,可在其中创建或添加现有成员VI。如LabVIEW类包含一个属性定义文件夹,可将LabVIEW类连接至属性节点访问私有数据。
  • 新建»基于动态分配模板的VI-LabVIEW将生成一个新成员VI,该VI带有错误输入簇、错误输出簇、一个用于错误处理的条件结构,以及输入LabVIEW类和输出LabVIEW类。在VI连线板上,LabVIEW将输入和输出接线端都设置为动态
  • 新建»基于静态分配模板的VI-LabVIEW将生成一个新成员VI,该VI带有错误输入簇、错误输出簇、一个用于错误处理的条件结构,以及输入LabVIEW类和输出LabVIEW类。与创建动态分配VI相反,LabVIEW不将动态分配VI的连线板上的输入和输出接线端设置为动态。
  • 新建»用于数据成员访问的VI-打开创建访问器对话框。通过该对话框快速创建用于访问LabVIEW类数据的成员VI。
    注:  使用该选项之前必须先保存新建的LabVIEW类。如未保存新类,LabVIEW将用于数据成员访问的VI选项灰暗显示。
  • 新建»用于重写的VI-创建一个重写祖先类成员VI的成员VI。LabVIEW使用父VI的图标对子类的图标进行覆盖,创建新VI的图标。
    注:  若不存在可重写的有效成员VI,LabVIEW将禁用新建»用于重写的VI选项。更多关于“动态VI”和“重写”的信息见继承一节。

右键单击前面板或程序框图上的常量或控件,从快捷菜单中选择显示类库,在项目浏览器窗口高亮显示相关类。如当前类不属于某个LabVIEW项目,LabVIEW将打开一个类窗口显示该类。

继承

通过“继承”可在现有类的基础上创建新类。若创建一个新的LabVIEW类并将它设置为继承另一个类的数据及成员VI,这个新类将可使用它所继承的类中“公共”及“保护”型的成员VI。这个新类还可添加自己的数据和成员VI以丰富其功能。例如,“汽车”类的私有数据包括齿轮数量车门数量颜色制造型号。如创建了一个名为卡车类的新类,可将卡车类设置为继承汽车类,并对卡车类添加布尔型数据短车厢?四轮驱动?。但是,如下图所示,在对LabVIEW类进行捆绑或解除捆绑时,节点上仅显示当前类私有数据的接线端,而不显示从祖先类继承的任何数据的接线端。

祖先类的数据是私有的,必须使用祖先类提供的函数(成员VI)才能修改这些数据。子孙类的成员VI可以调用祖先类任何“公共”型的成员VI,就像调用LabVIEW中的其它VI一样。子孙类的成员VI也可以调用祖先类“保护”型的成员VI。若指定一个祖先类成员VI为“保护”型,则其任何子类的成员VI可以调用其方法,但该类继承层次结构以外的任何其它VI都不能调用其方法。如需访问卡车类从汽车类继承而来的齿轮数量,可在汽车类中创建一个“公共”型或“保护”型的成员VI,比如Get Gears.vi。在Get Gears.vi的程序框图中可对汽车类解除捆绑,从而得到齿轮数量。然后将齿轮数量分配到连线板的一个输出接线端,用这种方法,汽车类的子孙类(例如,卡车类)就可访问汽车类的某个私有数据(例如,齿轮数量)了。

在汽车类中创建访问数据成员的成员VI,即可访问齿轮数量。创建成员VI时,勾选创建访问器对话框的通过属性节点实现复选框。然后,将卡车类连接至属性节点,右键单击属性接线端并选择选择属性»齿轮数量

注:  LabVIEW类不可调用另一个LabVIEW类的“私有”型成员VI,即使是父类,也无法调用其子类的私有成员VI。类的“私有”成员VI只能由这个类的其它成员VI在程序框图中调用。
注:  (FPGA模块)创建FPGA VI时,可在限制的范围内使用继承。

LabVIEW对象

短语LabVIEW对象是一个特定的类的名称。LabVIEW面向对象编程中,LabVIEW对象继承树的根类。默认状态下,所有LabVIEW类都是从“LabVIEW对象”继承而来的。通过“LabVIEW对象”创建的VI,能对多个LabVIEW类执行通用的操作。例如,可创建一个由若干LabVIEW类构成的数组,该数组的类型是某个基类,数组中的数据可以是这个基类或它的任何子孙类类型的元素,因此数组中的数据是异构的。如果一个数组的类型为“LabVIEW对象”,则该数组可包含汽车类、卡车类和保龄球类。保龄球类并不是从汽车类或卡车类继承而来的,因此LabVIEW将创建一个通用于这些类的最近的祖先基类,此处便以“LabVIEW对象”为基类。

下图显示了汽车类数组,一个包含汽车类和卡车类的数组。由于卡车类是从汽车类继承而来的,汽车类就是通用于这两个类的最近的祖先基类。该图还显示了包含“LabVIEW对象”类、汽车类、卡车类和保龄球类的LabVIEW对象数组。保龄球类不是从汽车类或卡车类继承而来的,但所有的这三个类都是从“LabVIEW对象”这个根类继承而来,因此LabVIEW对象数组的类型是“LabVIEW对象”。

设置继承

默认状态下,所有LabVIEW类都是从“LabVIEW对象”继承而来的。如果要更改一个类的继承关系,必须在创建该类之后更改继承。通过类属性对话框,可设置类的继承关系和其它选项。在LabVIEW类层次结构窗口中,可查看LabVIEW类的层次结构。类继承的层次结构可包括下列类型的类。

  • 父类-供其它LabVIEW类继承数据、“公共”型成员VI和“保护”型成员VI的LabVIEW类。
  • 子类-继承父类的公共和受保护成员VI的LabVIEW类。除非父类提供访问VI,否则子类不继承父类的私有数据。
  • 兄弟类-和一个LabVIEW类继承同一个父类的另一个LabVIEW类。
  • 祖先类-一个LabVIEW类的上一层(父类)、上二层(父类的父类)、上三层等等。“LabVIEW对象”是所有LabVIEW类的始祖。
  • 子孙类-一个LabVIEW类的下一层(子类)、下二层(子类的子类)、下三层等等。
注:  如创建的VI重写父级LabVIEW类中的属性访问器VI,则必须在子类和父类的属性定义文件夹中指定相同的名称和文件夹路径。

连线外观

类定义了新的数据类型。在程序框图中,通过类定义的数据类型采用默认的LabVIEW类连线外观,或者继承父类的连线外观。通过类属性对话框可对LabVIEW类更改连线外观。适当地更改不同LabVIEW类的连线外观,可提高程序框图的可读性。而使用过多的连线色彩和连线模式将破坏程序框图的可读性。下图左侧显示了LabVIEW内置的连线外观,右侧显示了自定义连线外观的样例。

关于在LabVIEW中避免过多连线和色彩的技巧,见LabVIEW Style Checklist

动态和静态分配成员VI

方法是在对象上执行的操作。在LabVIEW面向对象编程中,方法是用户创建的成员VI。成员VI在LabVIEW类的数据上进行运算。某些方法可用单个VI定义。这些方法称为静态分配方法,因为LabVIEW每次调用的是同一个VI。有时也可在类层次结构的多个VI中定义同名的方法。这些方法称为动态分配方法,因为直到运行才可确定LabVIEW调用的是哪一个VI。动态分配方法和多态VI类似。多态VI根据连入数据的类型来确定调用哪一个VI;动态分配方法在运行时根据输入接线端到达的数据确定调用类层次结构中的哪一个VI。

通过设置成员VI的连线板,成员VI既可指派为静态,也可指派为动态。若连线板上包含一个动态分配的输入接线端,则该成员VI是动态分配方法的一部分。如连线板上没有动态分配输入接线端,则该成员VI定义了一个静态分配方法。

一个LabVIEW类继承另一个LabVIEW类时,子类将继承父类中定义的所有“公共”和“保护”型的方法。通过在子类中创建和父类成员VI相同名称的成员VI,可定义该方法的子类实现。

由于LabVIEW通过单个VI定义静态分配方法,子类成员VI的名称不可与祖先类的静态分配成员VI的名称相同。例如,当父类“汽车”中包含了“开门”VI这一静态分配成员VI,则子类“卡车”便无法将名为“开门”的VI作为其成员VI。由于“卡车”从“汽车“继承了其成员VI,故“开门”VI这一方法已在“卡车”上定义。如在程序框图中将静态分配方法作为子VI调用,则调用这些静态分配方法和调用普通子VI没有任何区别。

对一个方法可定义多个动态VI,可在继承层次结构中的每一层对该方法定义一个动态分配VI。如动态分配成员VI在父类中定义,且也在子类中定义,则子类的执行将覆盖或扩展父类的执行。在以下范例中,“汽车”类和“卡车”类都定义了动态分配方法Set Make VI。若在程序框图中将一个动态分配VI作为子VI调用,在编辑状态下,该节点和一般子VI调用没有区别。然而如果运行VI,则流入动态分配输入接线端的数据将决定LabVIEW会调用类层次结构中的哪一个动态成员VI。LabVIEW类连线可传递本身及任意子类所允许的数据类型,不同的数据类型被定义了不同的成员VI,该节点将根据连线上的数据类型执行相应的动态分配VI。阅读下列范例。只有Set Make VI的“汽车”类在主VI的程序框图上。第一次循环时,由于“汽车”类的数据在类连线上,故LabVIEW执行Set Make VI的“汽车”类。第二次循环时,由于“卡车”类的数据在类连线上,故LabVIEW执行Set Make VI的“卡车”类。

如父类定义了一个动态分配VI但是不提供该VI的执行,每个子类必须覆盖其父VI。在许多情况下,用户需提供父类VI的有意义的动作。例如,如有一个Shape类,定义Area VI。Area VI返回Shape对象的面积。没有一个公式可计算任意形状的面积。所以,每个继承类必须用相应的面积计算公式覆盖Area VI。如创建了一个子类Circle,子类Circle就必须提供一个Area VI,计算pi * radius * radius

如不定义父类VI的执行,父VI就仅仅是所有覆盖VI必须匹配的连线板和VI属性。每个子类必须提供一个类似的覆盖VI。要确保LabVIEW强制执行该要求,可在父类中标示VI为子类必须覆盖的VI。

一些子类可能无法覆盖成员VI的功能。例如,如Shape类有子类Quadrilateral,无法提供四边形面积计算公式,除非知道四边形为特定类型的四边形。用户可使Quadrilateral类迁移覆盖要求至其子类,以避免在Quadrilateral类中创建Area VI的空执行。右键单击子类,从快捷菜单中选择属性,打开对象属性对话框。在继承页上,勾选将全部重写要求传递至子孙类复选框。LabVIEW将要求Quadrilateral的所有子类(例如,梯形和矩形)覆盖Area VI。

要求子孙类覆盖父类成员VI对成员VI的运行没有影响。

注:  LLB无法包含同名文件。因此,若类层次结构中存在同名的动态成员VI,这些VI不可放置在同一个LLB中。

注:  如子类的VI覆盖了父类的VI,子类的VI必须和父类的VI在以下方面吻合:重入设置首选执行设置优先级设置、连线板接线端、连线板模式,以及访问范围

双击程序框图上的一个动态分配子VI以显示选择实现对话框。通过该对话框可查看当前内存中动态分配子VI的所有实现,然后将该子VI的一个或多个实现打开

如选择新建»重写VI而创建一个VI以覆盖一个动态分配成员VI的父实现,则将创建另一个动态分配成员VI,因为重写父类成员VI的VI与其父类VI同名且具有动态分配接线端。LabVIEW会自动将调用父类方法节点和正确的动态分配输入和输出类接线端及其它匹配祖先类VI所需的接线端放在程序框图上。若不存在可重写的祖先类成员VI,则LabVIEW将禁用重写VI选项。

注:  (FPGA模块)创建FPGA VI时,可在限制的范围内使用动态分配。

动态分配输出

右键单击连线板上的输出接线端并选择动态分配输出(推荐),可将LabVIEW类的输出接线端标记为动态。将一个含有动态分配输出端的VI作为子VI调用时,动态分配输出端的数据将转换为与动态分配输入端相同的类型。例如,将汽车类连接到一个动态分配输入接线端,则该成员VI的输出数据类型将和输入数据类型相同,即为汽车类类型。动态分配输入端和动态分配输出端之间的数据是可修改的。然而,为了确保LabVIEW类运行时的安全,动态分配输入端的数据必须流入所有动态分配输出端。同时,为了确保LabVIEW仅从动态分配输入端读取一次,仅向动态分配输出端写入一次,不可将动态的程序框图接线端放置在结构之中。

注:  调试动态分配成员VI时,可检查动态分配输入端和动态分配输出端之间的连线是否出错。若一条连线从动态输入出发,且未通过任何能改变运行时数据类型的函数,则该连线的背景颜色将是灰色而不是通常的白色。若该连线通过一个能改变其数据类型的函数,则连线的背景颜色将变成红色。为使动态输出正常运作,不可改变LabVIEW类的数据类型。

若已知成员VI的程序框图中LabVIEW类的输出数据类型不同于输入数据类型,则须确保连线板上的LabVIEW类动态输出接线端设置为推荐而不是动态分配输出(推荐)。例如,若LabVIEW类的输入是汽车类而输出是卡车类,则必须改变连线板上LabVIEW类的默认接线端。此外,也可从空VI创建成员VI,此时可手动设定连线板接线端。

注:  如在一个有动态分配输入和动态分配输出的成员VI中使用条件结构或事件结构,不能在输出隧道上选择未连线时使用默认选项。若在输出隧道上使用未连线时使用默认,LabVIEW将断开该VI。必须连接结构中的所有分支。考虑将隧道配置为自动连接输入和输出隧道。

动态分配的内存拷贝优化

如上所述,创建动态分配的方法时,每个子类都会继承父类上定义的所有公共方法和受保护的方法。子类可覆盖或扩展这些成员VI。VI调用动态分配的方法时,LabVIEW只有当运行时才知道将调用哪个方法。LabVIEW将优化调用方VI的内存分配,并认为子类含有的任何成员VI的设置与父类中成员VI的设置相同。如父类VI的输入为常量,调用方将认为在所有子类VI中输入也是常量。如父类成员VI的输入返回为输出,调用方VI认为所有子类成员VI也会有相同的操作。

如上述假设有误,则优化失败。例如,如创建了一个成员VI,其中包含未改变的输入。这将使调用方VI认为所有输入都不改变,即使每次覆盖都会改变部分或所有输入。或者,如未将任何输入连接至父类成员VI的输出,调用方VI将认为没有任何输出与输入共享内存,即使在相邻的VI之间,上一个VI的输出可能是下一个VI的输入。LabVIEW必须创建代码来处理子类中导致优化失败的非预期行为。

更好的优化是指写入父类VI时,在子类VI中发生尽可能一致的操作。创建一个动态分配方法,可使用元素同址操作结构明确表示各个接线端的作用。将元素同址操作结构放在父类的动态分配的VI中,然后将元素同址操作结构节点对置于元素同址操作结构中。这些节点对用于通知LabVIEW哪些输入应连接至哪些输出,哪些输入应视为常量,哪些输入应视为修改过的量。然后,LabVIEW开始优化调用方VI。

关于动态分配接线端的使用,见下列项目范例。

  • labview\examples\Object-Oriented Programming\Dynamic Dispatching\Dynamic Dispatching.lvproj

  • labview\examples\Object-Oriented Programming\Dynamic Terminals\Dynamic Terminals.lvproj

文章是否对您有帮助?

没有帮助