类实例与类指针
声明类实例
类可以在全局变量、PROGRAM、FUNCTION、FUNCTION_BLOCK、其他类和结构体中实例化,也可以作为数组元素类型。
VAR_GLOBAL
GlobalCounter : cCounter;
END_VAR
PROGRAM Main
VAR
Counter : cCounter;
Counters : ARRAY[0..3] OF cCounter;
Wrapper : ST_CounterWrapper;
END_VAR
END_PROGRAM
TYPE ST_CounterWrapper :
STRUCT
Counter : cCounter;
END_STRUCT
END_TYPE
数组中的每个类元素、结构体中的类成员以及类中嵌套的其他类实例都有独立的成员存储。
声明类指针
使用 POINTER TO 声明类指针,使用 ADR() 获取实例地址,使用 ^ 解引用。
VAR
Counter : cCounter;
CounterPtr : POINTER TO cCounter;
Result : DINT;
END_VAR
CounterPtr := ADR(Counter);
Result := CounterPtr^.Add();
也可以为类指针定义类型别名或声明指针数组:
TYPE PCounter : POINTER TO cCounter;
END_TYPE
VAR
Pointers : ARRAY[0..3] OF PCounter;
END_VAR
标准引用类型 REF_TO
REF_TO 按 IEC 标准语法声明引用变量;使用 REF(变量) 获取引用,使用 ^ 解引用。引用变量可与兼容的 POINTER TO 变量互赋,派生类型引用可赋给 基类引用并按对象实际类型动态绑定。引用变量的初始值只能是 NULL;指针可用 NULLPTR 或 NULL 初始化。
VAR
Value : DINT;
ValueRef : REF_TO DINT := NULL;
END_VAR
ValueRef := REF(Value);
ValueRef^ := 5;
引用类型不支持加减运算,也不能彼此进行大小比较;相应表达式分别报 E3165 和 E3166。
多态调用
父类指针可以保存派生类实例的地址。通过指针调用被重写的方法时,编译器和运行时会根据实例的实际类型选择派生类实现。
VAR
Derived : cDouble;
BasePtr : POINTER TO cCounter;
Result : DINT;
END_VAR
BasePtr := ADR(Derived);
Result := BasePtr^.Add();
嵌套和链式访问
类实例、结构体成员和类指针可以组合访问:
result1 := wrapper.Counter.Add();
result2 := container.Child.Add();
result3 := container.CounterPtr^.Add();
指针安全
- 类指针应由兼容类型实例的
ADR()地址赋值; - 不 要将常量数字直接赋给类指针;
- 不要把
VAR_TEMP临时实例的地址保存到生命周期更长的全局指针中; - 解引用前应确认指针已经指向有效实例;
- 不要在无继承关系的类类型之间强制传递指针。
编译器会阻止无效的常量地址赋值,并对可能造成悬空指针的临时实例地址逃逸给出警告。无效指针解引用可能触发运行时异常保护。
危险
类指针直接访问 PLC 运行时内存。错误的地址或对象生命周期会造成不可预测的程序行为。优先使用普通类实例和 VAR_IN_OUT 引用参数,仅在确有需要时使用类指针。