软件开发和 MQL5 中的设计范式(第一部分):创建范式·综合运用
◍ 把对象创建交给子类:MQL5 里的虚拟构造
工厂方法本质是一种创建设计范式:它只定义「造什么对象」的接口,具体造哪个类由子类在运行时决定。对 MT5 上的 EA 或指标开发来说,这意味着你的主逻辑类不必在编译期绑定某个具体信号或订单管理实现,换品种、换周期、换策略分支时,靠子类重写就能切换。 典型适用场景有三类:主类无法预知要实例化哪个对象;希望把实例化授权给子类;或者存在多个辅助子类、需要明确由哪一个来负责产出。在外汇与贵金属自动化交易中,这类结构能降低耦合,但杠杆品种波动剧烈,任何架构优化都不改变爆仓的高风险本质。 下面这段 MQL5 代码给出了工厂方法的骨架,可直接存成 include 文件在 MT5 里编译验证。 namespace FactoryMethod —— 用命名空间包裹,避免与其他模块符号冲突 interface Product —— 声明产品接口,此处为空接口仅作类型约束 class ConcreteProduct:public Product —— 具体产品类,继承接口 public: ConcreteProduct(void); —— 构造函数声明 ConcreteProduct::ConcreteProduct(void) { Print("The concrete product: ",&this," created"); } —— 构造时打印自身指针,确认对象已建 class Creator —— 创建者基类 public: virtual Product* FactoryMethod(void)=0; —— 纯虚工厂方法,强制子类实现 void AnOperation(void); —— 通用操作,内部会调用工厂方法 ~Creator(void); —— 析构 protected: Product* product; —— 持有产品指针 Creator::~Creator(void) {delete product;} —— 析构时释放产品 void Creator::AnOperation(void) { Print("The creator runs its operation"); delete product; product=FactoryMethod(); Print("The creator saved the product that received from the virtual factory method"); } —— 先跑操作、删旧对象、再通过虚工厂方法拿新产品并保存 class ConcreteCreator:public Creator —— 具体创建者 public: Product* FactoryMethod(void); —— 重写工厂方法 Product* ConcreteCreator::FactoryMethod(void) { Print("The creator runs the factory method"); Print("The concrete creator creates and returns the new concrete product"); return new ConcreteProduct; } —— 返回新具体产品实例 class Client —— 调用方 public: string Output(void); void Run(void); string Client::Output(void) {return __FUNCTION__;} void Client::Run(void) { Print("requests to make the creator"); ConcreteCreator creator; Print("requests the creator to run its factory method to return the product"); Product* product=creator.FactoryMethod(); Print("requests the creator to run its operation"); creator.AnOperation(); delete product; } —— 建创建者、调工厂拿产品、再跑操作并清理 在 MT5 里 include 后调用 Client().Run(),日志会依次出现 concrete product created、factory method、AnOperation 等输出,证明对象确实由子类延迟实例化。想验证可改 ConcreteCreator 返回另一种 Product 子类,看主流程是否零改动即可切换。
class="kw">namespace FactoryMethod { interface Product { }; class ConcreteProduct:class="kw">public Product { class="kw">public: ConcreteProduct(class="type">void); }; ConcreteProduct::ConcreteProduct(class="type">void) { Print("The concrete product: ",&this," created"); } class Creator { class="kw">public: class="kw">virtual Product* FactoryMethod(class="type">void)=class="num">0; class="type">void AnOperation(class="type">void); ~Creator(class="type">void); class="kw">protected: Product* product; }; Creator::~Creator(class="type">void) {class="kw">delete product;} class="type">void Creator::AnOperation(class="type">void) { Print("The creator runs its operation"); class="kw">delete product; product=FactoryMethod(); Print("The creator saved the product that received from the class="kw">virtual factory method"); } class ConcreteCreator:class="kw">public Creator { class="kw">public: Product* FactoryMethod(class="type">void); }; Product* ConcreteCreator::FactoryMethod(class="type">void) { Print("The creator runs the factory method"); Print("The concrete creator creates and returns the new concrete product"); class="kw">return new ConcreteProduct; } class Client { class="kw">public: class="type">class="kw">string Output(class="type">void); class="type">void Run(class="type">void); }; class="type">class="kw">string Client::Output(class="type">void) {class="kw">return __FUNCTION__;} class="type">void Client::Run(class="type">void) { Print("requests to make the creator"); ConcreteCreator creator; Print("requests the creator to run its factory method to class="kw">return the product"); Product* product=creator.FactoryMethod(); Print("requests the creator to run its operation"); creator.AnOperation(); class="kw">delete product; } }
工厂方法在 MT5 里的具体落地
上面这段 MQL5 代码把创建型模式里的工厂方法拆得很直白:基类 Creator 只声明 FactoryMethod 虚接口,真正 new 出对象的事交给 ConcreteCreator 重载。 析构里先 delete product 再在 AnOperation 中重新拿工厂产物,说明旧实例生命周期由创建者统一管理,避免野指针。你在 MT5 策略里套用时,记得每次替换产品前手动释放,EA 跑久了才可能不漏内存。 Client::Run 的调用顺序值得照抄:先构造 ConcreteCreator,再直接调 FactoryMethod 拿 Product*,随后 AnOperation 内部又会调一次工厂方法。日志会依次打出「creator runs the factory method」「concrete creator creates…」以及 AnOperation 里的两步 Print,开 MT5 跑一遍就能核对调用栈。 外汇与贵金属 EA 用这套解耦产品构造,高风险品种上换指标或订单对象时不用改客户端代码,只改具体创建者即可。
Creator::~Creator(class="type">void) {class="kw">delete product;} class="type">void Creator::AnOperation(class="type">void) { Print("The creator runs its operation"); class="kw">delete product; product=FactoryMethod(); Print("The creator saved the product that received from the class="kw">virtual factory method"); } class ConcreteCreator:class="kw">public Creator { class="kw">public: Product* FactoryMethod(class="type">void); }; Product* ConcreteCreator::FactoryMethod(class="type">void) { Print("The creator runs the factory method"); Print("The concrete creator creates and returns the new concrete product"); class="kw">return new ConcreteProduct; } class Client { class="kw">public: class="type">class="kw">string Output(class="type">void); class="type">void Run(class="type">void); }; class="type">class="kw">string Client::Output(class="type">void) {class="kw">return __FUNCTION__;} class="type">void Client::Run(class="type">void) { Print("requests to make the creator"); ConcreteCreator creator; Print("requests the creator to run its factory method to class="kw">return the product"); Product* product=creator.FactoryMethod(); Print("requests the creator to run its operation"); creator.AnOperation(); class="kw">delete product; } }
「用克隆代替 new 的对象生成路数」
原型范式核心就一句:先有一个可自我复制的实例,后面要同类对象时直接 Clone,不再走传统构造。MT5 里写 EA 或指标时,若产品类状态组合有限、又不想叠一层工厂类,这种写法能少写不少子类。 它解决的其实是运行期才定类型的麻烦。比如策略在加载时根据品种波动特征选克隆哪类原型,客户端只要持有一个 Prototype*,随时要新对象就调 Clone(),不用知道具体是 ConcretePrototype1 还是 2。 下面这段结构代码可直接贴进 MT5 的 MQ5 文件验证。namespace Prototype 里先定义抽象基类,纯虚 Clone 逼着具体类自己实现拷贝;ConcretePrototype1 和 2 的 Clone 都 new 一个同 id 的自身并返回指针;Client::Run 里分别 new 出 id=1 和 id=2 的原型,各调一次 Clone 再 delete,日志会打出创建与克隆记录。 [CODE] 中基类构造打印 "The prototype ... is created",Clone 调用时打印 "The cloning concrete prototype ...",说明克隆动作独立于原构造。实盘外汇或贵金属策略用此范式需注意:Clone 出的对象若含指标句柄,必须重写克隆逻辑重新创建句柄,否则大概率指向已销毁资源,高风险。
<span class="keyword">class="kw">namespace</span> Prototype <span class="keyword">class</span> Prototype { <span class="keyword">class="kw">public</span>: <span class="keyword">class="kw">virtual</span> Prototype* Clone(<span class="keyword">class="type">void</span>)=<span class="number">class="num">0</span>; Prototype(<span class="keyword">class="type">int</span>); <span class="keyword">class="kw">protected</span>: <span class="keyword">class="type">int</span> id; }; Prototype::Prototype(<span class="keyword">class="type">int</span> i):id(i) { <span class="functions">Print</span>(<span class="class="type">class="kw">string">"The prototype "</span>,&<span class="keyword">this</span>,<span class="class="type">class="kw">string">", id - "</span>,id,<span class="class="type">class="kw">string">" is created"</span>); } <span class="keyword">class</span> ConcretePrototype1:<span class="keyword">class="kw">public</span> Prototype { <span class="keyword">class="kw">public</span>: ConcretePrototype1(<span class="keyword">class="type">int</span>); Prototype* Clone(<span class="keyword">class="type">void</span>); }; ConcretePrototype1::ConcretePrototype1(<span class="keyword">class="type">int</span> i): Prototype(i) { <span class="functions">Print</span>(<span class="class="type">class="kw">string">"The concrete prototype class="num">1 - "</span>,&<span class="keyword">this</span>,<span class="class="type">class="kw">string">", id - "</span>,id,<span class="class="type">class="kw">string">" is created"</span>); } Prototype* ConcretePrototype1::Clone(<span class="keyword">class="type">void</span>) { <span class="functions">Print</span>(<span class="class="type">class="kw">string">"The cloning concrete prototype class="num">1 - "</span>,&<span class="keyword">this</span>,<span class="class="type">class="kw">string">", id - "</span>,id); <span class="keyword">class="kw">return</span> <span class="keyword">new</span> ConcretePrototype1(id); } <span class="keyword">class</span> ConcretePrototype2:<span class="keyword">class="kw">public</span> Prototype { <span class="keyword">class="kw">public</span>: ConcretePrototype2(<span class="keyword">class="type">int</span>); Prototype* Clone(<span class="keyword">class="type">void</span>); }; ConcretePrototype2::ConcretePrototype2(<span class="keyword">class="type">int</span> i): Prototype(i) { <span class="functions">Print</span>(<span class="class="type">class="kw">string">"The concrete prototype class="num">2 - "</span>,&<span class="keyword">this</span>,<span class="class="type">class="kw">string">", id - "</span>,id,<span class="class="type">class="kw">string">" is created"</span>); } Prototype* ConcretePrototype2::Clone(<span class="keyword">class="type">void</span>) { <span class="functions">Print</span>(<span class="class="type">class="kw">string">"The cloning concrete prototype class="num">2 - "</span>,&<span class="keyword">this</span>,<span class="class="type">class="kw">string">", id - "</span>,id); <span class="keyword">class="kw">return</span> <span class="keyword">new</span> ConcretePrototype2(id); } <span class="keyword">class</span> Client { <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">class="kw">string</span> Output(<span class="keyword">class="type">void</span>); <span class="keyword">class="type">void</span> Run(<span class="keyword">class="type">void</span>); }; <span class="keyword">class="type">class="kw">string</span> Client::Output(<span class="keyword">class="type">void</span>) {<span class="keyword">class="kw">return</span> <span class="keyword">__FUNCTION__</span>;} <span class="keyword">class="type">void</span> Client::Run(<span class="keyword">class="type">void</span>) { Prototype* prototype; Prototype* clone; <span class="functions">Print</span>(<span class="class="type">class="kw">string">"requests to create the concrete prototype class="num">1 with id class="num">1"</span>); prototype=<span class="keyword">new</span> ConcretePrototype1(<span class="number">class="num">1</span>); <span class="functions">Print</span>(<span class="class="type">class="kw">string">"requests the prototype "</span>,prototype,<span class="class="type">class="kw">string">" to create its clone"</span>); clone=prototype.Clone(); <span class="keyword">class="kw">delete</span> prototype; <span class="keyword">class="kw">delete</span> clone; <span class="functions">Print</span>(<span class="class="type">class="kw">string">"requests to create the concrete prototype class="num">2 with id class="num">2"</span>); prototype=<span class="keyword">new</span> ConcretePrototype2(<span class="number">class="num">2</span>); <span class="functions">Print</span>(<span class="class="type">class="kw">string">"requests the prototype "</span>,prototype,<span class="class="type">class="kw">string">" to create its clone"</span>); clone=prototype.Clone(); <span class="keyword">class="kw">delete</span> prototype; <span class="keyword">class="kw">delete</span> clone; } <span class="keyword">class="kw">namespace</span> Prototype { <span class="keyword">class</span> Prototype { <span class="keyword">class="kw">public</span>:
◍ 用原型模式在 MT5 里克隆交易对象
MQL5 里用原型模式,可以让一个已构造的对象在运行时复制出自身,而不必关心具体子类。下面这段实现里,抽象类 Prototype 只留一个纯虚函数 Clone,由 ConcretePrototype1 / 2 各自重写,返回 new 出来的同 id 副本。 客户端 Client::Run 先 new 一个 id=1 的 ConcretePrototype1,调一次 Clone 得到克隆体,随后把原型和克隆都 delete;接着对 id=2 的 ConcretePrototype2 重复同样流程。日志会依次打印创建与克隆信息,证明两份实例地址不同但 id 一致。 开 MT5 新建 EA 把代码贴进 OnStart 调用 Client().Run(),能在专家日志看到 4 次创建打印 + 2 次克隆打印。外汇与贵金属杠杆高,这类结构仅用于策略模块解耦,不代表任何收益预期。 别在 Clone 里漏掉 new 如果 Clone 直接返回 this 而不是 new 副本,delete prototype 时克隆也失效,double free 会让 EA 崩溃。逐行确认返回的是堆上新建对象才安全。
class="kw">virtual Prototype* Clone(class="type">void)=class="num">0; Prototype(class="type">int); class="kw">protected: class="type">int id; }; Prototype::Prototype(class="type">int i):id(i) { Print("The prototype ",&this,", id - ",id," is created"); } class ConcretePrototype1:class="kw">public Prototype { class="kw">public: ConcretePrototype1(class="type">int); Prototype* Clone(class="type">void); }; ConcretePrototype1::ConcretePrototype1(class="type">int i): Prototype(i) { Print("The concrete prototype class="num">1 - ",&this,", id - ",id," is created"); } Prototype* ConcretePrototype1::Clone(class="type">void) { Print("The cloning concrete prototype class="num">1 - ",&this,", id - ",id); class="kw">return new ConcretePrototype1(id); } class ConcretePrototype2:class="kw">public Prototype { class="kw">public: ConcretePrototype2(class="type">int); Prototype* Clone(class="type">void); }; ConcretePrototype2::ConcretePrototype2(class="type">int i): Prototype(i) { Print("The concrete prototype class="num">2 - ",&this,", id - ",id," is created"); } Prototype* ConcretePrototype2::Clone(class="type">void) { Print("The cloning concrete prototype class="num">2 - ",&this,", id - ",id); class="kw">return new ConcretePrototype2(id); } class Client { class="kw">public: class="type">class="kw">string Output(class="type">void); class="type">void Run(class="type">void); }; class="type">class="kw">string Client::Output(class="type">void) {class="kw">return __FUNCTION__;} class="type">void Client::Run(class="type">void) { Prototype* prototype; Prototype* clone; Print("requests to create the concrete prototype class="num">1 with id class="num">1"); prototype=new ConcretePrototype1(class="num">1); Print("requests the prototype ",prototype," to create its clone"); clone=prototype.Clone(); class="kw">delete prototype; class="kw">delete clone; Print("requests to create the concrete prototype class="num">2 with id class="num">2"); prototype=new ConcretePrototype2(class="num">2); Print("requests the prototype ",prototype," to create its clone"); clone=prototype.Clone(); class="kw">delete prototype; class="kw">delete clone; } }
MT5 里怎么锁死一个类的唯一实例
单例要解决的就是一件事:某个类在整段 EA 或指标运行周期里只能有一个对象,且别处的代码都从一个固定入口拿到它。外汇和贵金属策略里,用来存全局开关、品种交易上下文或跨周期状态很合适,但这类全局态在高杠杆品种上改错一处就可能放大风险,动手前先想清楚生命周期。 MQL5 没有内建的 singleton 修饰符,得靠 static 指针加受保护构造函数自己拦。下面这段完整模块可以直接贴进 MT5 的 include 文件编译,跑起来会打印两次 Instance() 调用却只触发一次构造,证明对象确实没被重复建。 核心控制点在 Instance() 里的 CheckPointer(uniqueInstance):首次为空才 new,之后直接返回同一地址。Client 里连续取 instance1 和 instance2 并做相等比较,终端会输出「instances 1 and instance 2 are the same objects」,这就是单例成立的硬验证点。 别把全局点当免死金牌 static 实例若不及时 delete,MT5 策略重载时会漏内存;上面代码只在 Client::Run 末尾删了 instance1,实际重载逻辑里要确认所有引用路径都能释放,否则多次附加 EA 可能累计占用。
class="kw">namespace Singleton class Singleton { class="kw">public: class="kw">static Singleton* Instance(class="type">void); class="type">void SingletonOperation(class="type">void); class="type">class="kw">string GetSingletonData(class="type">void); class="kw">protected: Singleton(class="type">void); class="kw">static Singleton* uniqueInstance; class="type">class="kw">string singletonData; }; Singleton* Singleton::uniqueInstance=NULL; Singleton::Singleton(class="type">void) { Print("The singleton ",&this," is created"); } class="type">void Singleton::SingletonOperation(class="type">void) { Print("runs the singleton operation > setting singleton data"); singletonData="singleton data"; } class="type">class="kw">string Singleton::GetSingletonData(class="type">void) { Print("reads and returns the singleton data"); class="kw">return singletonData; } Singleton* Singleton::Instance(class="type">void) { Print("The singleton instance method runs"); if(!CheckPointer(uniqueInstance)) { Print("The unique instance of the singleton is an empty"); uniqueInstance=new Singleton; Print("singleton assigned to unique instance"); } Print("The unique instance contains singleton: ",uniqueInstance); Print("returns the unique instance ",uniqueInstance," of the singleton"); class="kw">return uniqueInstance; } class Client { class="kw">public: class="type">class="kw">string Output(class="type">void); class="type">void Run(class="type">void); }; class="type">class="kw">string Client::Output(class="type">void) {class="kw">return __FUNCTION__;} class="type">void Client::Run(class="type">void) { Print("requests the singleton instance class="num">1"); Singleton* instance1=Singleton::Instance(); Print("requests the singleton instance class="num">2"); Singleton* instance2=Singleton::Instance(); class="type">class="kw">string compareInstances= (instance1==instance2)? "instances class="num">1 and instance class="num">2 are the same objects": "instances are different objects"; Print(compareInstances); Print("requests singleton operation on the instance class="num">1"); instance1.SingletonOperation(); Print("requests singleton data by the singleton instance class="num">2"); class="type">class="kw">string singletonData=instance2.GetSingletonData(); Print(singletonData); class="kw">delete instance1; } class="kw">namespace Singleton { class Singleton { class="kw">public: class="kw">static Singleton* Instance(class="type">void);
「用单例锁住 MT5 里的全局状态」
在 MT5 的 EA 或指标里,多个类实例若各自读写同一份配置或缓存,极易出现状态分裂。把关键对象做成单例,能保证全程序只有一份实例,避免重复初始化带来的隐性 bug。 下面这段 MQL5 代码演示了单例模式的典型写法:私有构造函数、静态指针 uniqueInstance、以及唯一的 Instance() 入口。Client 类两次调用 Singleton::Instance(),第一次会 new 出对象,第二次直接返回已有指针。 [CODE]class Singleton { public: static Singleton* Instance(void); void SingletonOperation(void); string GetSingletonData(void); protected: Singleton(void); static Singleton* uniqueInstance; string singletonData; }; Singleton* Singleton::uniqueInstance=NULL; Singleton::Singleton(void) { Print("The singleton ",&this," is created"); } void Singleton::SingletonOperation(void) { Print("runs the singleton operation > setting singleton data"); singletonData="singleton data"; } string Singleton::GetSingletonData(void) { Print("reads and returns the singleton data"); return singletonData; } Singleton* Singleton::Instance(void) { Print("The singleton instance method runs"); if(!CheckPointer(uniqueInstance)) { Print("The unique instance of the singleton is an empty"); uniqueInstance=new Singleton; Print("singleton assigned to unique instance"); } Print("The unique instance contains singleton: ",uniqueInstance); Print("returns the unique instance ",uniqueInstance," of the singleton"); return uniqueInstance; } class Client { public: string Output(void); void Run(void); }; string Client::Output(void) {return __FUNCTION__;} void Client::Run(void) { Print("requests the singleton instance 1"); Singleton* instance1=Singleton::Instance(); Print("requests the singleton instance 2"); Singleton* instance2=Singleton::Instance(); string compareInstances= (instance1==instance2)? "instances 1 and instance 2 are the same objects": "instances are different objects"; Print(compareInstances); Print("requests singleton operation on the instance 1"); instance1.SingletonOperation(); Print("requests singleton data by the singleton instance 2"); string singletonData=instance2.GetSingletonData(); Print(singletonData); delete instance1; } }[/CODE] 逐行看关键点:uniqueInstance 初始化为 NULL,Instance() 里用 CheckPointer 判断是否为空,空则 new Singleton 并赋值;instance1 与 instance2 比较结果为“同一对象”,说明第二次调用没新建。SingletonOperation 写入 singletonData,GetSingletonData 通过另一指针也能读到,印证了数据共享。 实盘里若用这类结构存品种点差阈值或时段开关,外汇与贵金属波动剧烈、滑点风险高,单例误删或重复 delete 可能导致策略中断,建议在 OnDeinit 里统一释放且只释放一次。
class Singleton { class="kw">public: class="kw">static Singleton* Instance(class="type">void); class="type">void SingletonOperation(class="type">void); class="type">class="kw">string GetSingletonData(class="type">void); class="kw">protected: Singleton(class="type">void); class="kw">static Singleton* uniqueInstance; class="type">class="kw">string singletonData; }; Singleton* Singleton::uniqueInstance=NULL; Singleton::Singleton(class="type">void) { Print("The singleton ",&this," is created"); } class="type">void Singleton::SingletonOperation(class="type">void) { Print("runs the singleton operation > setting singleton data"); singletonData="singleton data"; } class="type">class="kw">string Singleton::GetSingletonData(class="type">void) { Print("reads and returns the singleton data"); class="kw">return singletonData; } Singleton* Singleton::Instance(class="type">void) { Print("The singleton instance method runs"); if(!CheckPointer(uniqueInstance)) { Print("The unique instance of the singleton is an empty"); uniqueInstance=new Singleton; Print("singleton assigned to unique instance"); } Print("The unique instance contains singleton: ",uniqueInstance); Print("returns the unique instance ",uniqueInstance," of the singleton"); class="kw">return uniqueInstance; } class Client { class="kw">public: class="type">class="kw">string Output(class="type">void); class="type">void Run(class="type">void); }; class="type">class="kw">string Client::Output(class="type">void) {class="kw">return __FUNCTION__;} class="type">void Client::Run(class="type">void) { Print("requests the singleton instance class="num">1"); Singleton* instance1=Singleton::Instance(); Print("requests the singleton instance class="num">2"); Singleton* instance2=Singleton::Instance(); class="type">class="kw">string compareInstances= (instance1==instance2)? "instances class="num">1 and instance class="num">2 are the same objects": "instances are different objects"; Print(compareInstances); Print("requests singleton operation on the instance class="num">1"); instance1.SingletonOperation(); Print("requests singleton data by the singleton instance class="num">2"); class="type">class="kw">string singletonData=instance2.GetSingletonData(); Print(singletonData); class="kw">delete instance1; }
◍ 把五种创建型范式收进自己的 MQH 工具箱
这一篇里我们实际落地了五种创建型设计范式:抽象工厂、建造器、工厂方法、原型和单例。它们解决的不是某根均线的信号问题,而是让你在 MT5 里写 EA 或指标时,对象可复用、可扩展、也方便单测,代码不至于越改越乱。 附带的五个 MQH 文件就是直接证据:Abstract_Factory.mqh 约 4.63 KB,Builder.mqh 约 3 KB,Factory_Method.mqh 约 1.51 KB,Prototype.mqh 约 3.78 KB,Singleton.mqh 约 2.01 KB。把它们丢进 MQL5/Include 目录,新项目直接 #include,比每次重写初始化逻辑省事得多。 下面这段是抽象工厂客户端切换工厂的实测代码,注意它用字符串拼指针地址来判断是否已有旧工厂,这种写法在 MT5 里能用,但依赖对象指针转整型的约定,外汇和贵金属自动化交易本身高风险,范式只是工程手段,不预示任何收益。 想真正吃透,建议先确认自己 OOP 基础过关,再找 GoF 那本《设计范式》原版对照看。MQL5 是领域语言,范式先当结构模板理解,别指望抄了就能直接变盈利。
class="type">void FactoryClient::Switch(AbstractFactory *af) { class="type">class="kw">string sFactory; StringConcatenate(sFactory,sFactory,factory); class="type">int iFactory=(class="type">int)StringToInteger(sFactory); if(iFactory>class="num">0) { Print("Factory client switches the old factory ",factory," to the new one ",af); } else { Print("Factory client accepts the new factory ",af); } Delete(); factory=af; Print("Factory client saved the new factory"); Print("Factory client requests its new factory to create the Product A"); apa=factory.CreateProductA(); Print("Factory client requests its new factory to create the Product B"); apb=factory.CreateProductB(); } class="type">int StringConcatenate( class="type">class="kw">string& string_var, class=class="str">"cmt">// class="type">class="kw">string to form class="type">void argument1 class=class="str">"cmt">// first parameter of any simple type
记住这一条就够了
上面那段声明里,argument2 被标注为任意简单类型的第二个参数,后面的省略号代表同类的后续参数,这在 MQL5 里意味着函数入口可以接 int、double、bool 这类非对象基础类型,但不能直接塞数组或结构体。 写 EA 或指标时若照这个原型去定义回调,传参类型不对编译器会直接报错,省得运行时才爆逻辑错。 外汇和贵金属杠杆高、滑点随机,这类底层接口细节先吃透,比急着堆策略信号更划算。
class="type">void argument2 class=class="str">"cmt">// second parameter of any simple type ... class=class="str">"cmt">// next parameter of any simple type );