类
创建
在项目资源管理器上,点击鼠标右键,选择 "新建 -> 类",就可以创建一个新的类:
我们给它起个名字叫做 “Parent”,因为我们将要使用 它当做另一个类的父类。创建新类的时候,LabVIEW 会询问新类继承自哪个父类:
作为我们创建的第一个类,我们并没有为它准备一个父类。LabVIEW 中所有的类都必须有且仅有一个父类,如果不需要设定一个特别的父类,那么就会默认使用 “LabVIEW 对象” 作为父类。所以 “LabVIEW 对象” 会是 LabVIEW 中所有类的祖先类,当你编写了一个 VI,用于处理所有 LabVIEW 的对象,比如得到对象的类名,那么输入数据的类型就可以采用 “LabVIEW 对象”,使得你的程序可以接受任何类型的实例。
接下来我们再使用同样的方法创建一个名为 “Child” 的类,它是 “Parent” 的子类,所以选择 “Parent” 作为它的父类:
类在结构上是一种特殊的 LabVIEW 库,因此它的很多属性和设置都与库相似。比如,类的名字也作为名字空间;也可以为类中的 VI 设置访问权限。除此之外,类还有它特殊的设置,比如有属性和方法等。
类被保存在一个以 lvclass 为后缀名的文件中。
方法(VI)
鼠标右键点击在类上,就可以为类创建方法了。方法本质上就是 VI。
在新建这一栏下可以看到很多条目:
- VI:就是指创建一个普通的方法 VI。
- 虚拟文件夹:如果类中的方法很多,为了便于管理,可以把它们归类到不同的文件夹中。
- 属性定义文件夹:这是专用来保存数据读写 VI 的文件夹。
- 基于动态分配模板的 VI:如果一个类中的方法,有可能被子类中相同的方法重写,就应该使用这个模板。相当于其它语言中的 “虚函数”。
- 基于静态分配模板的 VI:如果一个类中的方法,不允许被子类重写,就应该使用这个条目来创建。它与基于动态分配模板的 VI 的唯一区别在于:动态分配的 VI 的类输入输出接线端是动态分配的,而静态分配的 VI 则不是。
- 用于数据成员访问的 VI:因为类的数据全部是私有的,所以需要借助公有 VI 来访问它们。这个选项用于快速建立读写类中数据的 VI。这些 VI 依然是基于动态分配模板的 VI 或基于静态分配模板的 VI,只是 LabVIEW 帮忙在程序框图上添加了一些数据读写的代码。
- 用于重写的 VI:这个选项是专门给子类用的,用来创建重写父类的方法 VI。它创建的是一个基于动态分配模板的 VI,只是 LabVIEW 帮忙在程序框图上添加了一些调用父类同名方法的代码。
- 类型定义:创建用户自定义控件,用于自定义一些在模块里可能需要用到的数据类型。
下面我们研究一下 “基于动态分配模板的 VI” 和 “基于静态分配模板的 VI” 各自的行为是什么样的。
首先在 Parent 类中创建一个基于静态分配模板的 VI,叫做 static.vi。这个 VI 的功能比较简单, 只是返回一行文字 “Parent Static VI”。
之后我们尝试在 Child 类中创建一个同名的基于静态分配模板的 VI。这时候就会发现新建的 Child.lvclass:static.vi 是不能运行的。
点击它的运行按钮,可以看到错误信息:它试图重写一个祖先类中的基于静态分配模板的 VI。
如果一个类中已经有了一个基于静态分配模板的 VI,它的子孙类中就不能再有同名的方法了。
再创建两个基于动态分配模板的 VI,我们可以在父类和子类中分别创建出同名的基于动态分配模板的 VI。它们的区别仅在于输出文字略有不同,两个 VI 都可以正常运行:
现在我们可以写一个测试程序来看看上面几个 VI 都会返回什么结果?下图是一个简单的测试,它的程序框图上分别有一个父类的实例,和一个子类的实例,之后 分别把它们传递给上面创建的几个 VI,看看返回结果:
上图中,偏紫色图标的 VI 都属于 Parent 类,偏黄色图标的 VI 都属于 Child 类。
static.vi 由于是基于静态分配模板的 VI,不能被子孙类重写,所以可以确定,被调用的永远是 Parent 类中的那个 static.vi,返回值也一定是 “Parent Static VI”,不论输入的类型是哪一种。
Parent 类的实例调用 Parent.lvclass:dynamic.vi 返回值是 “Parent Dynamic VI”;Child 类的实例调用 Child.lvclass:dynamic.vi 返回值是 “Child Dynamic VI”。这也是比较确定的。
需要注意的是最后一条测试,“message 6” 中的文字。因为 Child 类继承自 Parent 类,Child 类可以被认为是 Parent 类的一个子集。如果一个对象属于 Child 类,也就必然属于 Parent 类。因此在程序中我们可以把这个 Child 类的对象的数据类型转化成为 Parent 类的数据类型,然后用它去调用 dynamic.vi。这里的实例是由 Child 类生成的,不论它在程序中使用哪个祖先类的数据类型表示它,它都始终还是一个 Child 类实例,所以程序调用它的 dynamic.vi,运行的一定还是 Child 类中的那个 dynamic.vi。我们可以看到这里返回的文字是 “Child Dynamic VI”。只有当子类中没有重写某个基于动态分配模板的 VI 时,程序才会调用它父类中的同名 VI。
下面我们再改动一下 Parent.lvclass:static.vi 的程序逻辑,让它去调用一下 Parent.lvclass:dynamic.vi: