首页 / 交易学院 / 小布知识库 / MQL5 对象指针:为什么需要、如何安全使用
MQL5 对象指针:为什么需要、如何安全使用
🔗

MQL5 对象指针:为什么需要、如何安全使用

指针、CheckPointer、函数重载,以及链表与多态为何离不开指针

MQL5 编程 难度 · 进阶 2026-07-09 12 分钟阅读
#MQL5#指针#OOP#内存管理#多态

一、MQL5 里的「引用」是什么

MQL5 没有 C/C++ 那种裸指针运算(不能 &p+1 乱跳),但有「引用」概念:函数参数加 & 表示传引用而非拷贝,数组、结构体、类对象常这样传,避免大对象复制开销。理解「引用是为了高效传参、而非为了地址运算」,能避开很多从 C++ 转来的误解。

另一层是「句柄(handle)」——指标、文件、图表、时间轴对象的整数代号。MQL5 用句柄间接引用终端内部资源,你拿到的不是对象本身,而是一把钥匙。这和指针完全不同:句柄由终端管理生命周期,你不能也不该手动释放除了指标句柄之外的资源。

二、用 & 传递数组与结构体

把大数组传给函数时,写 double &arr[] 或 MqlTick &ticks[],函数内对数组的修改会反映到外面,且免去了整段复制。时间系列 price[] 就是这么传进 OnCalculate 的。结构体同理:用 & 传 struct,函数里改字段,调用方直接看到结果。

struct Signal { double ma; double rsi; bool allow; };
void Compute(Signal &sig)        // 引用传递,修改外可见
{
   sig.ma = iMA(_Symbol,0,20,0,MODE_SMA,0);
   sig.allow = (sig.rsi > 50);
}

三、类与 new / delete

MQL5 支持面向对象,可以用 class 封装状态与行为。对象用 new 创建、delete 销毁;标准库里的 CTrade、CIndicators 都是类。和引用不同,new 出来的是堆对象,要自己负责 delete,否则内存泄漏——虽然在 EA 单次运行里影响不大,但长时间运行的脚本里会累积。

实战里常把「策略状态」封成一个类:持仓标记、上一次信号、过滤器状态都成了成员变量,OnTick 里调用成员方法更新。这比一堆全局变量清晰得多,也便于在多个 EA 间复用同一套逻辑。

四、标准库对象的生命周期

CTrade、CChart 这类标准库对象,通常作为 EA 的全局成员在 OnInit 构造一次,之后 OnTick/OnTrade 反复用。不要在每个 tick 里 new 一个 CTrade——既浪费又可能打乱其内部状态。把它们当「长期雇工」,而非「临时工」。

✦ 长生命周期对象只建一次
下单器、指标封装器、图表控制器,都在 OnInit 或全局构造里建一次,全程复用。反复 new/delete 既慢又容易在状态上出 bug。

五、常见坑:悬空与重复释放

两个典型错误:一是 delete 了还在用的对象,之后再访问就崩;二是同一个句柄被 IndicatorRelease 两次。MQL5 没有垃圾回收,内存与句柄都得你管。规则简单:谁创建谁释放,释放后置空,访问前判空。

六、与 MQL4 的区别

MQL4 几乎没有类与引用,数组传参是隐式引用且用法含糊;MQL5 明确区分值传递与引用传递,且引入完整 OOP。从 MQL4 迁过来的人,最大的坑是「以为传了副本其实改了原值」或反之。看清函数签名里有没有 &,就不会懵。

七、实战:用类管理策略状态

把下面的状态收进一个类,比散落全局变量稳得多:当前是否有持仓、上一次信号方向、过滤器是否放行、连续亏损计数。OnTick 里调用类的 Update(),类内部维护一切,EA 主逻辑只读结论。这样状态迁移有单点,调试时一眼看清。

class CStrategyState {
   bool   hasPos;
   int    lastSignal;
   int    consecLoss;
   void OnFill(bool win) { if(!win) consecLoss++; else consecLoss=0; }
};
CStrategyState st;   // 全局,OnInit 后全程复用

八、用句柄驱动指标:CopyBuffer 实战

引用概念最终要落到「用句柄拿数据」这件事上。指标在 OnInit 里用 iMA、iRSI、iCustom 拿到一个整数 handle 后,真正的数据并不在 handle 里,而在终端管理的指标缓冲区。要读它,必须用 CopyBuffer(handle, buffer_index, start_pos, count, array) 把一段值拷进你声明的数组,handle 只是开门的钥匙,而不是数据本身。

一个常见疏漏:CopyBuffer 的返回值是「实际拷贝到的元素个数」,不是成功与否的布尔。若 handle 还没准备好(指标仍在计算首根 K 线)、或请求的柱数超过可用范围,返回值会小于你期望的 count。稳健写法是在调用后检查 rates_total 是否足够,并给目标数组预先分配长度,避免用到未初始化的 0 值去发信号。

double maBuf[];
int maH = iMA(_Symbol, PERIOD_CURRENT, 14, 0, MODE_EMA, PRICE_CLOSE);
int copied = CopyBuffer(maH, 0, 0, 3, maBuf);
if(copied < 3) { /* 数据未就绪,跳过本 tick */ return; }
double maNow = maBuf[0], maPrev = maBuf[1];
✦ 钥匙不是货
handle 与数组是两件事:handle 是钥匙,CopyBuffer 才是开门取货。把 iMA 的返回值当成均线价格,是新手最常犯的引用误用——你拿到的是整数代号,不是价格。

九、引用语义与复制陷阱

当函数参数是 &arr[] 或 &struct 时,函数内改的是同一份内存;而普通值传参(如传递一个 double、int)则是拷贝,函数里改了不影响外面。这个区别决定 bug 形态:用引用传大结构体省内存且可回写,但一旦函数里把它改乱,调用方也会跟着乱,调试时要在「谁动了这块内存」上花心思。

另一个隐蔽点:MQL5 里字符串、动态数组的赋值默认是拷贝语义,即 a = b 之后改 a 不影响 b;但类对象若存的是引用或指针,多个地方指向同一实例就会互相影响。写 EA 最稳的纪律是:跨事件(OnTick、OnTrade、定时器)共享的状态,统一收进类成员,明确它是单例,别到处 new 副本,否则状态会出现「读到的不是最新那个」的诡异现象。

十、小结

✦ 引用为效率,句柄为钥匙,类为组织
把 MQL5 的三种『间接』分清:& 是高效传参、handle 是终端资源的钥匙、class 是组织状态的容器。各司其职,代码既快又稳。

常见问题

没有 C/C++ 那种裸指针运算,但有引用(函数参数加 & 传引用)和句柄(指标/文件/图表等资源的整数代号)。句柄由终端管生命周期,不能手动释放(指标句柄除外)。
加 & 表示传引用,函数内修改对外可见且免复制,适合大数组;不加则是值拷贝。时间序列 price[] 进 OnCalculate 就是引用传参。
new 出的堆对象要自己 delete,且别重复释放或访问已释放的。长期使用的标准库对象(CTrade 等)在 OnInit 建一次、全程复用即可。