派生功能块FB中如何覆盖基类定义的结构体变量
IEC 61131-3标准的OOP实现不支持派生类覆写基类的同值类型成员变量,属性重写也要求返回值类型、参数列表和基类完全匹配,两种写法无法编译是语法规则限制导致的,可通过以下两种成熟方案实现需求:
方案1:基类使用结构体指针+派生类初始化绑定扩展实例
这个方案改动量最小,完全兼容已经定义好的结构体继承体系:
- 把基类FB里的
Data成员从值类型改为指向BaseModuleDataStruct的指针类型 - 派生类FB里声明
ModuleWithExtraFunctionDataStruct类型的扩展数据成员,在FB_init初始化阶段把基类的Data指针指向派生类自己的扩展数据实例首地址 - 基类逻辑照常通过Data指针访问基础字段,派生类直接访问自己的扩展数据成员即可,配方映射直接绑定派生类的扩展数据实例地址
参考代码:
FUNCTION_BLOCK BaseModuleFB VAR Data : POINTER TO BaseModuleDataStruct; END_VAR FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB VAR ExtendedData : ModuleWithExtraFunctionDataStruct; END_VAR METHOD FB_init : BOOL VAR_INPUT bInitRetains : BOOL; bInCopyCode : BOOL; END_VAR // 绑定指针到扩展结构体实例 Data := ADR(ExtendedData);
注意:继承得到的结构体内存布局前半段和基类结构体完全一致,基类通过Data指针访问
position1这类基础字段不会出现内存偏移错误。派生类访问position2这类新增字段直接用ExtendedData.position2即可,不需要做指针强转,安全性更高。ExtendedData本身已经包含所有基类字段+新增字段,配方存储直接映射这块内存就能拿到全量参数,不需要拆分。
方案2:虚方法返回结构体引用
如果不想使用裸指针,可以用虚方法封装数据获取逻辑,类型安全性更高:
- 基类定义虚方法
GetData,返回值为BaseModuleDataStruct类型的引用 - 派生类重写该方法,返回自身持有的扩展结构体实例的引用(因为扩展结构体继承自基类结构体,返回值类型兼容,可正常通过编译)
- FB内部所有参数访问逻辑统一调用
GetData()获取结构体引用,不要直接操作底层数据成员
参考代码:
FUNCTION_BLOCK BaseModuleFB VAR BaseData : BaseModuleDataStruct; END_VAR METHOD GetData : REFERENCE TO BaseModuleDataStruct GetData REF= BaseData; FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB VAR ExtendedData : ModuleWithExtraFunctionDataStruct; END_VAR METHOD GetData : REFERENCE TO BaseModuleDataStruct GetData REF= ExtendedData;
这个方案全程由编译器做类型校验,不存在裸指针越界风险,稳定性更好。基类逻辑不需要感知当前实例类型,调用
GetData().position1就能拿到正确的参数值,派生类访问新增字段直接操作ExtendedData即可,配方同样直接映射ExtendedData内存块即可。
避坑说明
- 不要在派生类里单独定义类似
ExtData的成员存新增参数:会导致基类逻辑无法统一读写参数,配方也要拆分多块存储,破坏多态一致性 - 不要在基类结构体里提前预留冗余字段存扩展参数:会造成内存浪费,不同派生类的同一段冗余字段含义不统一,后续维护成本极高
内容的提问来源于stack exchange,提问作者Stefan Roelofs
相关产品推荐
相关产品推荐

