MQL5 对象生命周期:创建与析构顺序
全局/局部/动态三类对象的创建与析构规则与避坑
MQL5 基于面向对象编程,对象分全局、局部、动态三类,析构都按“与构造相反”的顺序。理解这套规则,是写出不漏内存、不崩盘的 EA 的基础。
一、三类对象的创建与析构
- 全局对象:先声明先构造,退出时反向析构;
- 局部对象:进入程序块才创建,退出块时逆序析构;
- 动态对象:用 new 创建、delete 删除,时机完全由你掌控。
二、全局与局部的统一规则
无论全局还是局部,析构都按与构造相反的顺序进行;若某程序块没执行(如 input 开关为 false),里面的局部对象根本不会被创建。
三、动态对象的责任在你
- new 出来的对象必须 delete;
- 同一对象只能删一次;
- 删前用 CheckPointer 判断有效性;
- 重复 delete 会报 delete invalid pointer。
四、指针数组的坑
多个指针数组指向同一 new 对象时,只能删一次,第二次删报无效指针;漏删则日志报 N undeleted objects left。
五、组合类的正确姿势
- 类里含其他对象指针时,析构函数调用保护型 Destroy();
- 用 CheckPointer 规避无效指针;
- 安排好成员与原对象的声明/析构先后;
- 用 EA 日志定位错误管理。
六、生命周期与资源释放
指标句柄、文件句柄都要成对释放:OnInit/OnStart 里创建,OnDeinit 里 IndicatorRelease / FileClose,否则反复加载会泄漏。图表对象(OBJ_*)在图表关闭时由终端自动删除,但切换品种或周期会重建,别在对象里缓存跨周期状态。
七、OnInitialize 与 OnDeinitialize 的时机
MQL5 程序的生命周期由终端管理:加载时调 OnInit(指标)或 OnInitialize(EA/脚本),卸载或重编译时调 OnDeinit / OnDeinitialize。资源的申请放前者、释放放后者,成对出现才不会泄漏。
实务中常见错误:在 OnInit 里打开了文件或指标句柄,却忘了在 OnDeinit 里 FileClose / IndicatorRelease,下一次加载就句柄堆积、文件被占用。把「每次初始化申请了什么」列个清单,对应写在反初始化里释放,是最稳的纪律。
八、标准库对象的构造与析构实践
CTrade、CChart、CArray 这类标准库对象,通常作为 EA 的全局成员,在 OnInit 构造一次、全程复用,不要在 OnTick 里反复 new。析构由终端在卸载时自动调用,你只需保证它们引用的底层句柄已被正确释放。
九、资源泄漏的常见来源与排查
三类最易漏:一是文件句柄(FileOpen 后没 Close,多次加载后写不进);二是指标句柄(iCustom 反复调用不释放,句柄号耗尽);三是动态数组越界导致程序异常退出,析构来不及跑。
排查方法:在 OnDeinit 里打印当前还持有几个文件/句柄,或在任务管理器看终端句柄数是否随 reload 单调上涨。定位到具体资源后,补上对称的释放代码即可。