在 MQL5 中使用对象指针(基础篇)
◍ 用对象指针接管 MT5 图形资源
在 MQL5 里画图不靠全局函数硬写,而是用对象指针去持有和管理图表上的每一个元素。把指标线、箭头、文本都当成可被引用的实例,代码结构和后期维护会清爽很多。
官方示例自 2013-09-13 发布以来累计查看 3587 次、评论 41 条,说明这套对象化写法在 MT5 开发者里长期有参考价值。
声明一个 CObject 派生类的指针后,用 new 在堆上创建实例,再通过成员函数设置坐标、颜色、样式。记得在 OnDeinit 里 delete 掉,否则 EA 重载会留下孤儿对象拖慢终端。
自动建对象还是用指针,差别在哪
MQL5 里写自己的类之后,变量有两种来历:直接声明类类型,系统自动把对象建好并初始化;或者先拿指针,再用 new 显式动态开对象。两者生命周期和内存归属完全不同,选错容易在 EA 重载或函数退出时踩坑。 自动对象随作用域生灭,适合只在当前函数或结构体里短暂用的轻量实例;动态对象活在那块堆内存上,除非你手动 delete,否则一直占着。外汇和贵金属杠杆高、行情跳得快,EA 频繁重算时漏删指针可能慢慢吃掉内存,这类风险得提前防。 那什么时候非用指针不可?后面会先拆几个容易写错的使用场景,再给对应改法。
「悬空指针让EA在初始化阶段直接暴毙」
在 MT5 里用对象指针,第一铁律是先 new 再调用。指针变量声明了不等于对象存在,若直接拿去访问成员,运行时触发关键性错误,MQL 程序会被终端强制卸载,不是报错继续跑,而是整个 EA 从图表消失。 上面这段示例把坑摆得很直白:全局声明了 CHello *pstatus,但故意不写 pstatus = new CHello()。OnInit() 里第一行就 Print(pstatus.GetMessage()),对象根本没分配。实盘挂 EURUSD H1 测试,日志会先显示 loaded successfully,紧接着 Initializing failed,然后 removed——从加载到移除通常在同一秒内完成。 小工程里这种错一眼能瞅出来,可当策略代码膨胀到几千行、对象生命周期跟行情结构挂钩时,悬空指针就会变成幽灵 bug。尤其当某个指针只在特定 K 线形态下才被实例化,平时不碰、偶发一碰就崩,复盘日志都难定位。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetCriticalError.mq5 | class=class="str">"cmt">//| Copyright class="num">2010, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "class="num">2009, MetaQuotes Software Corp." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 一个简单的类 | class=class="str">"cmt">//+------------------------------------------------------------------+ class CHello { class="kw">private: class="type">class="kw">string m_message; class="kw">public: CHello(){m_message="开始...";} class="type">class="kw">string GetMessage(){class="kw">return(m_message);} }; class=class="str">"cmt">//--- CHello *pstatus; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- 调用方法显示状态 Print(pstatus.GetMessage()); class=class="str">"cmt">//--- 如果EA被成功初始化则打印消息 Print(__FUNCTION__," OnInit() 函数完成"); class=class="str">"cmt">//--- class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA订单函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//--- } class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetCriticalError.mq5 | class=class="str">"cmt">//| Copyright class="num">2010, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "class="num">2009, MetaQuotes Software Corp." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 一个简单的类 | class=class="str">"cmt">//+------------------------------------------------------------------+ class CHello { class="kw">private: class="type">class="kw">string m_message; class="kw">public: CHello(){m_message="开始...";} class="type">class="kw">string GetMessage(){class="kw">return(m_message);} }; class=class="str">"cmt">//--- CHello *pstatus; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- 调用方法显示状态 Print(pstatus.GetMessage()); class=class="str">"cmt">//--- 如果EA被成功初始化则打印消息 Print(__FUNCTION__," OnInit() 函数完成"); class=class="str">"cmt">//--- class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA订单函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//--- } class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 指针上路前先过一道安检
想在 MT5 里避免 EA 因空指针直接崩掉?只要在调用对象方法前加一道指针合法性检查就够了。把检查逻辑封装进 PrintStatus,传入 CHello 对象指针后先用 CheckPointer 判断,无效就打日志,有效才取消息。 实测把示例存成 GetCriticalError_OnDemand.mq5,外部参数 GetStop 默认 false 时,日志会稳定输出『PrintStatus 变量"对象"没有被初始化!』和『OnInit 函数已完成』,EA 不会终止。把 GetStop 设 true 则跳过检查直接 pstatus.GetMessage(),此时可能触发关键性错误让程序中断。 外汇与贵金属 EA 运行环境波动大,未初始化指针是常见隐性雷。养成『传指针必查 POINTER_INVALID』的习惯,能显著降低回测与实盘切换时的非预期停止概率。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 使用 CHello 类型对象的方法打印一条消息 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void PrintStatus(CHello *pobject) { if(CheckPointer(pobject)==POINTER_INVALID) Print(__FUNCTION__," 变量 &class="macro">#x27;对象&class="macro">#x27; 没有被初始化!"); else Print(pobject.GetMessage()); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetCriticalError_OnDemand.mq5 | class=class="str">"cmt">//| Copyright class="num">2010, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "class="num">2009, MetaQuotes Software Corp." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="kw">input class="type">bool GetStop=false;class=class="str">"cmt">// 获得关键性错误 class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 一个简单的类 | class=class="str">"cmt">//+------------------------------------------------------------------+ class CHello { class="kw">private: class="type">class="kw">string m_message; class="kw">public: CHello(){m_message="开始...";} class="type">class="kw">string GetMessage(){class="kw">return(m_message);} }; class=class="str">"cmt">//--- CHello *pstatus; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 使用CHello类型对象的方法打印消息 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void PrintStatus(CHello *pobject) { if(CheckPointer(pobject)==POINTER_INVALID) Print(__FUNCTION__," 变量 &class="macro">#x27;对象&class="macro">#x27; 没有被初始化!"); else Print(pobject.GetMessage()); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- 调用方法显示状态 if(GetStop) pstatus.GetMessage(); else PrintStatus(pstatus); class=class="str">"cmt">//--- 如果EA被成功初始化则打印一条消息
空 OnTick 与 OnInit 收尾的骨架意义
这段 MQL5 片段把 EA 的生命周期收在 OnInit 的日志输出和空 OnTick 上:Print(__FUNCTION__," OnInit() 函数完成") 只在初始化时打一行,返回 0 表示启动无误;OnTick 函数体为空,说明行情跳动时暂不执行任何逻辑。 对想验证骨架的人,直接把这段代码贴进 MT5 的 EA 编辑器编译运行,终端 Experts 标签会显示「EA名 OnInit() 函数完成」一次,之后 tick 驱动无任何动作。外汇与贵金属品种上挂这种空壳 EA 虽不交易,但能确认环境加载正常,属于零风险的结构验证。 写 EA 别急着填策略,先让 OnInit 返回 0、OnTick 留空跑通,再逐步塞入信号判断,比直接堆代码更容易排错。
Print(__FUNCTION__," OnInit() 函数完成"); class=class="str">"cmt">//--- class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA订单函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//--- } class=class="str">"cmt">//+------------------------------------------------------------------+