面向对象的编程语言,例如C#、C++、Java和UML等,采用术语“类”和“对象”。在面向对象的PLC可编程语言IEC 61131-3第三版中也采用类似的术语。面向对象的类是功能块的特例,功能块从基类继承。
类是用于面向对象编程的POU。一个类包括基本的变量和方法。一个类在它的方法被调用或它的变量被存取前需要实例化。类的本质是类型,而不是数据,因此,它不存放在内存中,不能被直接操作,只能实例化为对象后才能变得可操作。
类是具有相同属性和行为的一组对象的集合。类是对象的模板,类的实例是对象。它是将不同类型的数据和与这些数据相关的操作封装在一起的集合体。类中的数据具有隐藏性,类还具有封装性。类用于确定一类对象的行为。这些行为是通过类的内部数据结构和相关的操作来确定。行为通过一种操作接口来描述。用户只关心接口的功能(即类的各个成员函数的功能),操作接口是该类对象向其他对象所提供的服务。
类具有下列概念:
1)一个数据结构的定义,它分为公用和内部变量。
2)对该数据结构元素操作的方法。
3)由方法(算法)和数据结构组成。数据结构用于描述类的属性,方法用于描述类的行为和服务。
4)方法原型和实施界面的接口。
5)接口和类继承。
6)类的实例化。
1.类的声明
(1)类的声明
类的声明格式如下:
CLASS类名和修饰符
类的变量声明和方法的声明。
END_CLASS
【例1-44】类的声明。
(2)类的变量声明
类变量声明时的注意事项如下:
1)与功能块类似,VAR_EXTERNAL声明段所声明的变量值可在类的内部修改,而VAR_EXTERNALCONSTANT声明段所声明的变量值不能在类的内部修改。
2)如果需要,用VAR…END_VAR声明段来声明类的变量名和类型。类的变量可初始化。
3)RETAIN和NON_RETAIN限定符用于类的内部变量。
4)VAR声明段声明的静态变量可以具有公用(PUBLIC)属性。在类的外部,一个公用(PUBLIC)属性的变量可用存取功能块输出相同的语法进行存取。
5)变量段声明的变量可具有PUBLIC、PRIVATE、INTERNAL或PROTECTED存取属性。系统默认的存取属性是PROTECTED。
6)一个类可支持其他类的继承来扩展一个基本类。一个类可实施一个或多个接口。
7)其他功能块、类和面向对象的功能块的实例能够在VAR和VAR_EXTERNAL变量段内声明。
8)表1-20序号11描述的星号“*”可用于一个类的内部变量的声明。
9)一个类内声明的类的实例不能用相同的名称作为(相同命名空间的)函数名,以防止相互混淆。
类与功能块的区别如下:
1)类用CLASS…END_CLASS结构声明,功能块用FUNCTION_BLOCK…END_FUNCTION_BLOCK结构声明。
2)类的变量只在VAR段声明,不允许在VAR_INPUT、VAR_OUTPUT、VAR_IN_OUT、VAR_TEMP段声明。
3)类没有本体,一个类可只定义方法。
4)不可能调用一个类的实例,但类的方法可以被调用。
(3)类的性能
表1-64显示类的性能。
表1-64 类的性能
(续)
(4)类实例的声明
类实例的声明可用定义结构变量的类似方式来声明。
一个类的实例被声明时,类实例声明中公有变量初始值可在括号内的初始化列表中赋值,它跟在一个赋值符后,如表1-65序号2所示,在类标识符后面。初始化列表没有赋值的元素有类声明的初始值。
表1-65 类实例的声明
2.类的方法
面向对象编程语言中,方法用于在类的定义中定义可选语言元素的集合。在类中,方法用于定义类实例中数据执行的操作和服务。
(1)签名
签名(Signature)是用于用显式方式标识方法的参数接口的信息集。方法包括方法名称、类型和所有它的参数(即输入变量、输出变量和返回值结果的数据类型)的序列。一个签名由下列部分组成:
1)方法名。
2)返回值(结果)的数据类型。
3)方法的访问权限(存取属性)、可选的修饰符(附加属性)。
4)变量名、数据类型和所有它的变量,即输入、输出和输入-输出变量的序列。
内部变量(即就地变量)不是签名的一部分。VAR_EXTERNAL变量和常量也与签名无关。此外,公有PUBLIC或私有PRIVATE的存取规定也与签名无关。两个方法的签名不一致表示两个方法是不同的方法。
(2)方法的声明
一个类可以有一组方法。方法的声明应遵循下列规则:
1)方法在类的范围内声明。方法的声明可用本标准规定的任何一种编程语言定义。
2)方法对实例数据的操作有不同的存取属性,即PUBLIC(公有)、PRIVATE(私有)、INTERNAL(内部)和PROTECTED(保护)。若没有给出存取声明符的方法,具有默认的PROTECTED存取属性。
3)方法声明中可以声明它自己的变量,包括VAR_INPUT、VAR、VAR_TEMP、VAR_OUTPUT、VAR_IN_OUT和一个方法的返回值结果。方法的声明列写在类的变量声明段后面。
4)方法可选用附加属性。它们是OVERRIDE(覆盖)或ABSTRACT(抽象)。但方法的覆盖属性不在IEC 61131-3的本部分范围中。
与函数的声明类似,方法的声明应包含下列内容:
1)方法名。它用于标识方法。
2)方法的返回值数据类型。与函数类似,方法可有返回值和没有返回值。如果有返回值,则应声明其数据类型。
3)参数列表。在方法中采用的各参数(形式参数)列表。每个参数应有其变量名,数据类型和(如果用户约定,可设置的)初始值。方法参数用逗号分隔,在一个圆括号内列出。空括号表示没有方法参数。
4)方法的本体,即方法需要执行的操作。
(3)方法的执行
方法的执行应遵循下列规则:
1)方法被执行时,一个方法可以读它的输入和用它的暂存变量来计算它的输出和它的返回值结果。
2)与函数类似,方法的返回值赋值给方法名。IEC 61131-3第三版规定函数可以没有返回值,同样,方法也可以没有返回值。例如,C1.method1(inm1:=A,outm1=>Y);调用类C1的方法method1,没有返回值,但方法的输出送Y。
3)与函数类似,方法的所有变量和返回值都是暂存的,即其值在方法的本次执行到下一次执行之间是不被存储的。因此,方法输出变量的求值只能在方法调用的时刻进行,在前后两次调用之间保持其值。
4)每个方法和类的变量名应是唯一的。但由于就地变量不存储,因此不同方法的就地变量名可相同。
5)所有方法可对在类内声明的静态和外部变量进行读和写的存取。
6)所有变量和返回值结果可以是多值的,即可以是一个数组或一个结构变量。与函数类似,方法的返回值结果可作为表达式的一个操作数。
7)方法执行时,方法可调用在该类内定义的其他方法。该类实例的方法用THIS关键字调用。
【例1-45】方法的执行。
1)定义类名name。
该类name定义两个方法name_1和name_i。
2)方法的图形格式描述。
图1-33是方法的图形格式描述。图中name_C是类name的实例名。name.name_1和name.name_i是类name_C的方法名。
图1-33 方法的图形格式描述
inputs和outputs是两个方法的输入和输出变量名,因在不同的方法中,因此,可以采用相同的变量名。在执行时,用实际参数A1和A2替代输入,用实际参数Y1和Y2替代输出。R1和R2是用于存放方法的返回值结果的变量。
(4)调用同一类实例中的一个方法
这种方法调用时,在被调用方法前加THIS关键字,并用英文句号“.”将它与方法名分隔。
从方法所在类实例内的方法调用格式如下:
THIS.被调用方法名(参数列表);
关键字THIS不能用于不同类的实例,例如,不允许表达式:myInstance.THIS。
【例1-46】同一类实例中对本类内的一个方法的调用。
类COUNTER中有两个方法UP和UP5。UP5调用UP时用UP5:=THIS.UP(INC:=5,QU=>QU);实现本类的实例内方法UP的调用。
从方法所在类实例内的方法调用采用关键字THIS。THIS可以传递一个接口类型的变量。
【例1-47】用THIS实现接口类型变量的传递。
实例名*/
(5)调用方法所在类实例的基类实例中的方法
调用方法所在类实例的基类实例中的方法采用SUPER关键字。需指出,这种方法调用在程序执行前已经确定,因此,是静态绑定。
关键字SUPER不能用于另一个类实例,例如,不允许用表达式:my_Room.SUPER.DAYTIME()。它也不能用于进一步的外部调用,例如,不支持用SUPER.SUPER.DAYTIME()。
有效的基类实例的一个方法可用SUPER从其子类的方法来调用。
调用基类实例的方法的格式如下:
SUPER.被调用方法名(参数列表);
例如,SUPER.DAYTIME()。
【例1-48】用SUPER实现基类的方法调用。
示例说明如何调用基类的方法和如何覆盖基类的方法。
示例中,LIGHT2ROOM类继承LIGHTROOM,因此,LIGHTROOM是基类。当LIGHT2ROOM中的方法调用基类LIGHTROOM的方法DAYTIME和NIGHTTIME时,可采用SUPER关键字。这里,用覆盖基类方法来调用基类的方法。覆盖的有关概念见下述。
(6)外部调用不同类实例的方法
这是另一个类的一个实例对不是该类实例的一个方法的调用,也称为外部调用。调用时,先列出被调用类的类实例名,“.”和该被调用方法名及参数列表。外部调用可在实例声明时,由功能块本体或一个方法来发布。(www.xing528.com)
调用格式如下:
被调用类的类实例名.被调用方法名(参数列表);
【例1-49】不同类的方法调用。
CNT_1是COUNTER类的实例名,而COUNTER5与COUNTER是不同的类,因此,从类COUNTER5调用COUNTER类实例CNT_1的方法UP是方法的外部调用。
调用基类实例的方法和不同类实例的方法的外部调用有下列区别:
1)调用基类实例的方法的类与所在类是有继承关系的,即从子类的方法调用父类的方法,用SUPER.方法名()调用。
2)外部调用的类与所在类没有继承关系。因此,需要所在类声明外部的类的实例名(作为变量声明),并用该实例名.方法名()调用。
方法调用与函数调用的区别如下:
1)函数可直接用函数名调用。例如,A:=SQRT(6.3)。图形格式调用时,函数矩形框外部没有标注,矩形框内只标注函数名。
2)方法的调用分为本类实例内部调用、基类实例的方法调用和不同类实例的外部方法调用。必须在被调用的方法前声明。图形格式描述时,矩形框内上部标注类名、“.”和方法名。文本格式调用时,应注意下列事项:
①如果是内部调用,则加关键字THIS,例如,UP5:=THIS.UP(INC:=5,QU=>C)。图形格式描述时,在该方法的矩形框外上部必须标注THIS。矩形框内上部标注类名、“.”和方法名。
②如果是外部调用,则加外部类实例名,例如,UP5:=CNT_1.UP(INC:=5,QU=>C)。外部类的实例应包含该被调用的方法。图形格式描述时,在该方法的矩形框外上部必须标注类的实例名。矩形框内上部标注类名、“.”和方法名。
方法的调用可采用文本格式描述,也可用图形格式描述。与函数的格式和非格式调用类似,方法也有格式化调用和非格式化调用两种。表1-66是方法格式调用和非格式调用的示例。
表1-66 方法的格式调用和非格式调用(仅文本编程语言)
与函数的变量和返回值不具有存储功能一样,方法的变量和返回值也不具有存储功能。因此,不能直接用方法的输出,而需要将其先存储在一个变量。例如,需要调用“VALUE:=CT.UP(INC:=5,QU=>LIMIT);”,而不能直接用“VALUE:=CT.UP”。
3.类的继承
类是具有相同属性和操作的一组对象的集合。继承是指一般类的属性和操作传递给另一类。与函数和功能块类似,引入继承的目的是为代码的复用提供有效手段。因此,继承是一个类的定义可基于另一个已经存在的类,即子类基于父类,实现父类代码的复用。
(1)基类和派生类
如果一个类A继承自另一个类B,则称A类为“B的子类”,而把B类称为“A的父类”。继承可使子类具有父类的各种属性和方法,而不需要再次编写相同的代码。而子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能。
“基类”表示所有的祖先,即它们的父类和它们父类的父类等。
从一个已经存在的类(基类)用关键字EXTENDS扩展(派生)的类称为派生类。派生的类自动具有现有类的全部属性和特性,同时,它可添加原有类所没有的新的属性和特性,因此,派生类是原有类的扩展。
Class Children Extends Father;表示类Children是从类Father扩展而来。因此,Father是父类,Children是派生类,即子类。从一个基类派生的继承称为单继承,从多个基类派生的继承称为多继承。
派生类继承方式有公有、私有、内部和保护继承等。本标准在建立派生类时应遵循下列规则:
1)派生类继承不需要再声明来自它的基类声明中已经声明的所有方法(如果有的话),但有下列例外:
①不能私有(PRIVATE)继承,即派生类不能访问基类的具有私有属性的方法。
②在命名空间外部,不能内部(INTERNAL)继承。
2)由于派生类自动具有现有类的全部属性和特性,因此,派生类继承不需要再声明来自它的基类声明中已经声明的所有变量(如果有的话)。
3)本标准不支持多继承。因此,派生类只能继承一个基类。
4)为实现多继承,采用关键字IMPLEMENTS,用接口来实现。即继承只继承一个类,但可实现一个或多个接口。例如,Class A Extends B Implements C,D,E表示类A是从类B扩展,它有三个接口C、D、E。
5)派生类可扩展基类,即它可以使用基类的各种属性和方法,同时,也可以定义自己的属性和方法,并创建新的功能。
6)类继承具有传递性,即作为基类的类本身可以是一个派生类。这样的扩展可重复。
7)当基类的定义改变时,所有它所派生的类(和它们的子类)也改变它们的功能。
(2)覆盖方法
子类可继承父类中的方法,而不需要重新编写相同的方法。如果子类不想原封不动地继承父类的方法,而需做一定修改,这就需要采用方法的重写,也称为覆盖方法。子类中的新方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。
多态性是面向对象程序设计的核心概念。多态指不同类对象收到同一消息可产生完全不同的响应效果,即同一消息在不同接收对象有不同的调用方法。覆盖父类的方法是实现多态性的一种方式。
覆盖基类的方法应遵循下列规则:
1)三同原则:即子类的新方法和父类的方法有相同的方法名、相同的返回值数据类型和相同的参数列表。
2)覆盖基类的方法不能缩小父类方法的存取访问级别。
3)需要调用父类的原有方法,可使用SUPER()关键字。
4)派生类可访问同一命名空间的具有公有(PUBLIC)、保护(PROTECTED)或内部(INTERNAL)属性的基类的方法。
5)覆盖后的方法应有相同的存取属性,但对一个覆盖的方法,可使用FINAL的存取属性,表示该新的方法不能再被继承类的方法覆盖。由于FINAL属性表示最终功能,因此,具有FINAL属性的方法不能被覆盖。例如,METHOD FINAL MM表示该子类的MM方法具有FINAL属性,因此,由该子类扩展的子类不能对其方法MM进行覆盖。
方法的覆盖用下列格式:
METHOD OVERRIDE方法名
格式中的方法名是父类中的方法名。
【例1-50】覆盖方法。
END_CLASS
示例说明类CIRCLE2继承类CIRCLE,但它的方法PI覆盖被继承的方法PI。
图1-34说明类的继承和方法的覆盖。
图1-34 类的继承和方法的覆盖
图中,类X含方法MA、MB、MC,它们继承其父类,用圆括号表示,MD是类X自己的方法。类X1继承类X,箭头指向父类。因此,类X1继承了父类X的所有方法,即MA、MB、MC和MD,但覆盖方法MB,因此,在图中,没有用圆括号表示。类似地,类X11继承类X1,并覆盖方法MC,增加了自己的方法ME。类X12也继承类X1,覆盖方法MD,增加方法MF。
(3)类和方法的FINAL修饰
带FINAL修饰符的方法不能覆盖。一个带FINAL修饰符的类不能是基类。METHOD FINAL MB;表示方法MB不能被覆盖。类似地,CLASS FINAL C1;表示类C1是从父类继承的子类,它不是基类,但它不能再派生出其子类。
4.动态名绑定和多继承
(1)动态名绑定
绑定是计算机访问的方式,用于确定用什么方式对子类调用或属性被子类访问、函数调用和函数本身之间的关联、成员访问与变量内存地址之间的关系等。方法绑定指一个方法的调用与方法所在的类(方法主体)的关联。
方法(名)的绑定是与方法实现的方法名有关的。方法绑定是该方法被调用时该方法关联其方法本体的过程。程序执行前的方法名绑定(例如用编译器)被称为“静态绑定”或“前期绑定”,即程序运行前已经加载到内存。程序已经执行后实现的方法名绑定称为“动态绑定”或“后期绑定”,它是程序运行时将方法与其方法本体的绑定。动态绑定是将方法名与类实例的实际类型的一种方法建立联系。动态绑定的优点是在运行前可进行多种选择绑定,但动态名绑定需要在执行过程中进行绑定对象和编译,因此,执行效率要低些。
【例1-51】动态名绑定。
从程序可见,当调用CIRCLE的类实例CIR1中的方法CL时,需要关联该类实例CIR1的方法PI,即调用低精度圆周率。而调用CIRCLE2的类实例CIR2中的方法CL时,需要关联该类实例CIR2的方法PI,即调用高精度圆周率。因此,这种调用是在程序执行过程中将方法与有关方法本体建立联系的,因此,称为动态绑定。
IEC61131标准采用接口实现多继承。
【例1-52】多态性和动态绑定的实现。
有关程序如例1-51,再编写程序如下:
可以看到,调用My_Room_Ctrl实例时,根据RM的赋值,实现了不同的实现。在方法调用时,才确定方法所关联的类实例,因此,是动态绑定。
(2)多继承
本标准采用单继承,因此,多继承用接口实现。
【例1-53】多态性和动态绑定的实现。
有关程序如例1-48,再编写程序如下:
可以看到,调用My_Room_Ctrl实例时,根据RM的赋值,实现了不同的实现。由于在方法调用时,才确定方法所关联的类实例,因此,是动态绑定。
5.抽象类和抽象方法
一个类如果不与具体对象相联系,而只表达一个抽象概念,仅作为其派生类的一个基类,则这个类称为抽象类。抽象类用ABSTRACT限定。在抽象类中用ABSTRACT声明的方法称为抽象方法。
(1)抽象类
用ABSTRACT限定的类是抽象类。例如,CLASS ABSTRACT ROOM表示ROOM是抽象类。如果一个类中没有包含足够信息来描述一个具体的对象,这种类就是抽象类。
抽象类具有下列性能:
1)抽象类不能直接被实例化。因为抽象类没有包含具体实现的方法,因此,不能直接实例化。
2)抽象类必须作为派生其他类的基类使用,不能直接创建类实例。它可为派生类提供接口规范。
3)抽象类至少包含一个抽象方法,非抽象类不能包含抽象方法。
4)抽象类可用于作为输入或输入-输出变量类型。
5)从抽象类派生的非抽象类应包含所有继承的抽象方法的实际实现。
6)从抽象类派生的子类,可通过覆盖父类的抽象方法,来实现或不实现抽象方法。如果方法不能实现,则子类仍是抽象类,必须用ABSTRACT声明。如果对父类的所有抽象方法都有具体实现,则该子类才成为非抽象类。
7)抽象类的用途是让其派生类来继承其特性。它为多个派生类提供可共享的基类定义的公用特性。抽象类不能创建对象。
(2)抽象方法
用ABSTRACT限定的方法是抽象方法。例如,METHOD PUBLIC ABSTRACT M1表示M1是公有的抽象方法。抽象方法是只有方法声明,没有具体方法实现的一类方法。这表示抽象方法只有返回值的数据类型、方法名和它的参数,并不需要方法的实现。为实现抽象方法,必须建立子类,并将该方法覆盖,在覆盖时建立它的实现。
抽象方法具有下列性能:
1)抽象方法必须在抽象类中声明,必须用关键字ABSTRACT限定。
2)抽象方法不提供实现。因此,抽象方法没有方法本体。
3)抽象方法必须用子类的方法覆盖,将该抽象方法修正为包含方法本体,然后才能实现。
4)抽象方法的用途是为子类对该方法覆盖,并完成其方法的实现。只有子类可以实现父类的所有抽象方法。
6.可访问权限
(1)方法的可访问性
对每个方法,应规定方法调用的范围,即方法的可访问性或存取属性。方法可访问权限用存取符号PUBLIC(公有)、PRIVATE(私有)、PROTECTED(保护)和INTERNAL(内部)界定。如果没有规定权限,系统默认可访问权限是PROTECTED。图1-35显示四种可访问权限的调用。
图1-35 方法的可访问权限调用
1)PUBLIC:“公有”可访问权限指该方法对所有该类被应用的任何地方都可访问。例如,可从类外调用M1。具有PUBLIC可访问权限的方法可被所有该类所应用的地方进行访问。例如,从该方法所在的命名空间外、命名空间内和该类的内部进行访问。方法原型的访问通常是隐含的PUBLIC。因此,方法原型没有使用存取符。
2)PRIVATE:“私有”可访问权限指只在其定义类的内部可以访问该方法。即在方法定义的类内的其他属性和操作都可以对该方法进行访问,例如,调用M2。具有PRIVATE可访问权限的方法只能由该方法所在类的内部进行访问。
3)INTERNAL:“内部”可访问权限指只能从被声明的类的命名空间内可访问的方法。例如,调用M3。具有INTERNAL可访问权限的方法只能在该方法所在命名空间内部进行访问。
4)PROTECTED:“保护”可访问权限指唯一能够在类的内部和从所有它的派生类可访问的方法。但对于外部其他类,该方法则具有私有属性,例如,调用M4。具有PROTECTED可访问权限的方法只能从该方法所在类内部和它的派生类进行访问。
(2)变量的可访问性
在面向对象的编程语言中,变量应在变量声明段声明变量的可访问属性或存取属性。变量的可访问属性用可访问权限符号PUBLIC(公有)、PRIVATE(私有)、PROTECTED(保护)和INTERNAL(内部)限定。变量的可访问属性与变量的附加属性可以任意顺序组合。例如,VAR PUBLIC RETAIN表示在该变量段声明的变量具有PUBLIC可访问权限和掉电保持属性。
1)PUBLIC:“公有”可访问权限指它们是在该类被应用的任何地方都可访问的变量。
2)PRIVATE:“私有”可访问权限指它们是只在该类内部可访问的变量。没有实现继承时,默认属性为“私有”可访问权限,并且可省略。
3)INTERNAL:如果实现命名空间,则适用存取符INTERNAL。“内部”可访问权限指它们是只能从类声明的命名空间内可访问的变量。
4)PROTECTED:如果实现继承,则适用存取符PROTECTED。“保护”可访问权限指它们是唯一的能在类内部和从所有派生类访问的变量。系统默认的变量可访问性是PROTECTED。如果实现继承,但没有使用,则“保护”可访问权限和“私有”可访问权限有相同的影响。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。