跳到主要内容

类实例与类指针

声明类实例​

类可以在全局变量、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 引用参数,仅在确有需要时使用类指针。