//--- Example for receiving class//---class CAnyClass : public CAnyBaseClass // of course CAnyBaseClass inherits from CObject in the end too
{
private:
CWhatEver m_object; // embedded object
CFurther m_further; // embedded object
public:
//+------------------------------------------------------------------+//| Creation |//+------------------------------------------------------------------+
CAnyClass(void)
{
m_classname="CAnyClass"; //--- Connect side chains
m_object.CustomEventReceiver(PTR(this));
m_further.CustomEventReceiver(PTR(this));
}
protected:
//+------------------------------------------------------------------+//| Custom event handler for side chain messages |//+------------------------------------------------------------------+virtualvoid OnCustomEvent(CObject * sender, int eventid)
{
if (sender==PTR(m_object))
{
switch (eventid)
{
case123456:
Print("Here we go with 123456");
break;
//...
}
}
elseif (sender==PTR(m_further))
{
//...
}
}
};
我不想说你的做法是完全错误的,但你确实这样做了,因为这是结构编程,而不是OOP。最大的区别在于继承和重载的力量。顺便说一下,你不能真正继承真正的图形对象,但你可以把任何东西表示成一个代码对象,并从这个对象中引用一条线或其他东西。这就是通常在任何类中的做法,不管是MFC还是MQL类,它都是一样的。
如果你的行是对象,那么就把它们当作对象。不要在外面处理数组,在类的集合中处理,用指针工作。看一下CWndContainer,对它有一个概念。这个类是一个容器,主要管理CWnd对象的指针数组。再往前走一步,你的结构应该是。
CObject作为每个对象的基础
CPriceTimeObjects作为每个基于价格/时间的对象的基础,比如线,派生自CObject。它控制创建,持有时间和价格,并调用OnCreate(),这可以被下一个继承者使用。它也有一个Tick函数,调用虚拟的OnTick(),然后被继承者重载。
CTrendLine作为趋势线的基础,继承自CPriceTimeObjects并处理OnCreate,它使用ObjectCreate函数 创建最终线。它还应该有一个OnTick()处理程序来对Tick事件做出反应/回应,因为据我所知,它应该是对价格敏感的。
除此之外,你还有一个容器类,它管理着一个指针数组,里面有你想要的所有CTimePriceObject对象,它本身也继承自CTimePriceObject,并将OnTick()传递给它的 "子"。容器也有一个处理OnChartEvent()的函数,用于添加或删除线条。它还应该有一个扫描函数来扫描所有现有的对象,以备不时之需,专家是在线条被创建后添加的。此外,它处理来自CTimePrice的重载OnTick(),在那里循环数组,询问其中的每个CTrendLine对象,如果它觉得有责任通过调用每个子对象的Tick函数来做出反应,这是由虚拟OnTick处理的。为什么又是这样?因为CTrendLine也从CTimePrice重载了这个函数,这样这个类也可以被更多的继承者继承,有更多的函数。
非常好,多雷克。
但我有一些问题。
为什么容纳CTimePriceObject的容器必须从CTimePriceObject继承自己?例如,标准库中的 CIndicators 继承自 CArrayObj,并且很可能在不继承它的情况下引用 CIndicator。
我知道它想使用标准库而不是普通的数组,但是关于某种对象的容器应该继承对象本身的概念,我不清楚(因为容器本身不是它所包含的对象,即没有 "是 "的关系),尽管我觉得这是对的。
能否请你澄清一下你的观点?
第二,当你说CTrendLine使用语言原生的ObjectCreate函数创建最后一行时,你似乎是在从头开始建立框架,而不依赖标准库。
你什么时候会建议扩展标准库本身,例如,假设我想让标准库中的所有Indicator对象能够说出在过去的X个柱子中价格触及了多少次缓冲区
在过去的X个柱子里有多少次触及缓冲区。你会怎么做呢?因为如果你扩展了一个基类,如 CIndicator 或 CIndicatorBuffer,那么你的所有标准库的附属对象都必须继承自你的
新的CIndicator或新的CBuffer,例如CiMA。如果metaquotes在其标准的CIndicatorBuffer或以上的类中添加了一些东西,你会把所有这些自定义指标类复制到你的文件夹中,并把它们的继承权改为新的CIndicatorBuffer吗?
非常感谢您的见解。
BR
Willbur:
...
当你这样做的时候,我的SmartLine对象应该出现在MT5的菜单中,旁边还有趋势线、箭头、文本对象 和所有这些东西。
...
如果MT5允许这样做,那么我们就必须讨论剩下的问题,即当价格变化时,终端程序如何触发该对象。
...非常好,Dorek。
不过我有一些问题。
为什么容纳CTimePriceObject的容器必须继承自CTimePriceObject?例如,标准库中的CIndicators继承自CArrayObj,而且很可能在不继承它的情况下引用了CIndicator。
我知道它想使用标准库而不是普通的数组,但是关于某种对象的容器应该继承对象本身的概念,我不清楚(因为容器本身不是它所包含的对象,即没有 "是 "的关系),尽管我觉得这是对的。
能否请你澄清一下你的观点?
第二,当你说CTrendLine使用语言原生的ObjectCreate函数创建最后一行时,你似乎是在从头开始建立框架,而不依赖标准库。
你什么时候会建议扩展标准库本身,例如,假设我想让标准库中的所有Indicator对象能够说出在过去的X个柱子中价格触及了多少次缓冲区
在过去的X个柱子里有多少次触及缓冲区。你会怎么做呢?因为如果你扩展了一个基类,如 CIndicator 或 CIndicatorBuffer,那么你的所有标准库的附属对象都必须继承自你的
新的CIndicator或新的CBuffer,例如CiMA。如果metaquotes在其标准的CIndicatorBuffer或以上的类中添加了一些东西,你会把所有这些自定义指标类复制到你的文件夹中,并把它们的继承权改为新的CIndicatorBuffer吗?
非常感谢您的见解。
BR
1.容器的继承性。
MQL的一个限制是,你不能有多个继承人。正因为如此,一个人至少要死一次。当然你是对的,从CArrayObj继承也是有意义的,但我不会那样做,因为容器处理所有CTimePrice对象处理的事件,例如Tick()->OnTick(),并将它们分发给它的子对象,而数组对象与此无关。一个容器对象持有同样继承自同一基类的其他对象,只是有更多的共同点。这样一个容器也可以有一个基于价格和时间的锚,当你移动/转变这样一个容器时,它的工作也是移动它所有的 "子对象"。这只是一个例子,但这种想法的清单可能比关于数组功能的想法的清单还要长。
是的,也是这样,我真的创建了这样的类,处理许多类型的对象,同时我的经验告诉我,用这种方式管理继承是正确的决定。
2.2.从头开始的框架。
这里类似。当我开始深入研究标准库时,我发现了许多我不喜欢的东西。不仅是那些糟糕的性能,而且还缺乏灵活性,还因为不完整的架构。例如,我缺少一个全局的CMouse类/对象,以及一个介于CWnd和CObject之间的类,因为CWnd对象和线条一样都是图表的子对象,而且没有连接到这些对象,也没有像我上面描述的那样最终实现任何这些对象。而且,也没有一个主对象,它持有所有这些图表对象,从而可以用一个命令显示/隐藏所有这些对象,销毁它们,等等。CCanvas,同样的,一个很好的类,但在哪里与CWnd一起实现,使我能够在继承自CWnd的位图的基础上创建互动对象?等等。
此外,所有标准库的整个结构都不允许侧链,但这是必要的,因为MQL不允许无效指针。比如说。我正在使用一个名为CDragLine的类,它允许我创建可以被拖动的趋势线对象。如果这样的趋势线对象连接到一个订单,并进一步连接到一个面板/显示什么的,我需要使用这样一个侧链的可能性,使连接的面板也得到关于运动/变化的信息,这是由订单的变化引起的。反之亦然,我需要有移动订单的选项,并在拖动线条时通知面板。这种三角形的信息传递不能用标准库来 完成。这是由侧链完成的,这意味着所有的对象都继承自CObject的高级版本。这个类有可能将任何其他对象连接到一个对象,并向这样的连接对象发送消息。这样一来,无需任何奇怪的代码就可以实现更复杂和有效的信息传递。
我不能给大家任何建议,但实际上我决定放弃99%的标准库,我从原版中留下的唯一的类是CCanvas(但有一些变化和错误修正,见代码库)和CSymbolInfo。
--------------
如果有人对侧链的功能感兴趣,这里是我的CObject类的代码。如果你在每次更新后替换掉Object.mqh中的原始CObject,标准库的大部分部分就会得到这个侧链功能的增强。顺便说一下,节点连接也被实现了--这在原版中是没有做到的。
要添加这样的侧链功能,请在接收类里面做如下工作。
发送类CWhatEver和CFurther不知道任何关于接收者的信息,不知道是否有接收者。代码就是如下。
这里是CObject的替换。
谢谢你的阐述和分享侧链的想法。
坦率地说,我不太明白,而且代码不能编译(我把CWhatEver和CFurther创建为空类,直接继承了你发布的只有构造函数的CObject,只是为了了解情况)。
收到2个编译错误,关于CWhatEver::CWhatEver不能调用私有成员函数,对于CFurther也有同样的错误。
总之,我在谷歌上搜索了 "侧链 "一词,但没有找到。 如果你碰巧知道一些我可以阅读的书面材料,那就太好了。
BR
是的,这些类是空的,这就是为什么它们不能被编译。这些只是占位符。
侧链可能不是官方的描述,而是我对无效指针和/或多重继承的干净的MQL解决方法。简而言之。它让你有能力在任何类之间进行交流,如果对象被嵌入到类中,它是非常有用的,因为你可以 "告诉 "他们,包含的类是他们自定义事件 的接收器。这样你就可以避免任何面条式的编码。
例如,如果你有这样的东西
return OnClick()。
这样的事件会先调用最后一个派生类的重载,你可以把它扩展为如下内容
return OnClick()&CustomEvent(CUSTOM_CLICK)。
这样一来,不仅派生类通常有一个
虚拟 bool OnClick()
函数会被通知,包括该对象的类也能收到点击通知。
你的意思是,它是一种在任何两个对象之间进行通信的方式,而不是通过需要知道它要发送消息的另一个对象的通常的消息传递,而是通过自定义事件?
在你的CAnyClass的代码中,你有两个私有对象。你说的嵌入是指它们的主体也应该被贴在CAnyClass中吗?
CAnyClass,还是可以在外面定义?我的意思是,你所说的嵌入是指在另一个对象中的一个私有对象?
例如,如果我在CAnyClass外面写了CWhatEver,我想给CAnyClass发送一个事件的消息,你的意思是在CWhatEver中我将返回
return OnClick()&CustomEvent(CUSTOM_CLICK); // <====你的意思是&&而不是& ????
这将会像往常一样给CWhatEver的派生类和包含类CAnyClass(因为有自定义事件)发送信息?
为什么CObject被定义在一个#ifndef里面?
BTW,只是好奇,你的CObject中stdlib.mqh的目的是什么?
从下到上...
- stdlib,它不是包括在原始的object.mqh中吗?我在这里不需要它,但它包含CompareDouble(),这是比较两个双数的唯一可靠方法。
- 我使用#ifndef来避免标准的双数定义,并用于有条件的编译
- 是的,对于三角形的信息传递
- 二进制的&和逻辑版本的&&是一样的,结果都是bool。
- 我所说的嵌入是指,如果一个外国类的实例/对象是另一个类的一部分,并且两者不互相派生。使用OnClick()的例子,假设m_object以某种方式处理OnClick()的内部目的,例如,如果它是一个按钮或其他。如果你需要在CAnyClass中知道有一个点击,而没有这个自定义事件,代码会是什么样子?
说实话,这不是一个每天都需要的功能,但如果有一种情况,你不仅需要沟通方向,这就是解决方案。
回到最初的线程问题上。我们的想法是使用数组来管理多个对价格有反应的线。这些线对象在它们/它们的线被蜡烛穿过时处理事件,并强制采取一些行动。我建议使用一个容器。如果这个容器现在想计算这些动作,只是举个例子,当容器告诉每个对象它是自定义事件的接收者时,可以很容易地这样做,比如说使用
m_trendline[n].CustomEventReceiver(PTR(this))。
而CTrendLine类--当然--必须以某种方式实现它,像这样。
return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED)。
不幸的是, 我去 度假 了两个星期。飞机 几小时 后起飞(好吧, 可能会更糟)。
在这一点上 我 已经 有一个 问题。 有什么地方 有关于 MQL 框架 工作 的详细文件吗?
威尔伯
..
在这一点上 我 已经 有一个 问题。是否 有 关于 MQL 框架 工作 的详细文件?
没有 :-(
根据我的经验,学习研究 "mql框架 "是很好的。但正如Doerk所说,标准库有 很多问题,在我看来,它不能用于严肃的大项目。
从下到上...
- stdlib,它不是包括在原始的object.mqh中吗?我在这里不需要它,但它包含CompareDouble(),这是比较两个双数的唯一可靠方法。
- 我使用#ifndef来避免标准的双数定义,并用于有条件的编译
- 是的,对于三角形的信息传递
- 二进制的&和逻辑版本的&&是一样的,结果都是bool。
- 我所说的嵌入是指,如果一个外国类的实例/对象是另一个类的一部分,并且两者不互相派生。使用OnClick()的例子,假设m_object以某种方式处理OnClick()的内部目的,例如,如果它是一个按钮或其他。如果你需要在CAnyClass中知道有一个点击,而没有这个自定义事件,代码会是什么样子?
说实话,这不是一个每天都需要的功能,但如果有一种情况,你不仅需要沟通方向,这就是解决方案。
回到最初的线程问题上。我们的想法是使用数组来管理多个对价格有反应的线。这些线对象在它们/它们的线被蜡烛穿过时处理事件,并强制采取一些行动。我建议使用一个容器。如果这个容器现在想计算这些动作,只是举个例子,当容器告诉每个对象它是自定义事件的接收者时,可以很容易地这样做,比如说使用
m_trendline[n].CustomEventReceiver(PTR(this))。
而CTrendLine类--当然--必须以某种方式实现它,像这样。
return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED)。
stdlib我认为只是MQL4,但也许我错了。
对不起,我不明白为什么整个CObject必须放在#ifndef里面。如果它被写成纯文本,那么什么会被双重定义?还有,你为什么把CStruct作为空类放在CObject上面?
关于嵌入式类,(对不起,我也是做了25年的程序员,但不是OO)你的例子是指这样的情况,比如?假设我是一个CCar类,并且我嵌入了一个CWheel类。
CWheel有一个事件处理程序来处理空气压力的最小值或其他什么,而我作为汽车需要知道这些?
如果你能找到与这个例子相似的地方,或者其他具体的,因为我还不太清楚哪个类会触发事件(在你给出的点击的例子中,显然是图表)和谁来处理。甚至在技术上,理解这些线条(我想它们的目的是显示容器将如何做管理趋势线穿越价格的工作)。
m_trendline[n].CustomEventReceiver(PTR(this))。
如果是这样,容器现在处理的是什么类型的一般事件?
能否像一个新的柱子或价格变化有可能触发CTRENDLINE_CROSSED事件?为什么它要向每一行发送GetPointer(this)?
该行是否需要回调容器,以及为什么?
所以,如果是这样的话。
return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED)。
然后每条线将调用它自己的OnLineCrossed() - 这应该是一个普通的方法,只是检查价格是否已经被该特定的线所跨越。
然后调用CustomEvent(CTRENDLINE_CROSSED)--它的作用只是调用ChartCustomEvent(...)来触发事件CTRENLINE_CROSSED--在你的结构中,它将在容器中再次被处理? 最后一部分是使三角通信的原因,或者我只是在这里混合了两个不同的概念。那么容器将有一个处理CTRENDLINE_CROSSED的CustomEvent处理器?
写到这里,我觉得有些东西变得更清楚了(如果你愿意,可以不考虑CCar,只关注趋势线),但我还是不明白为什么要把GetPointer(this)移到每条线上?例如,在什么样的事件中,容器将调用每条线的自定义事件接收器,以检测价格变化,三角通信在哪里,或者它只适用于之前的三角例子(与CAnyClass)?
你能在这个趋势线上应用三角形的例子吗?
我真的很感谢你的时间、耐心和迄今为止的帮助,这一点都不明显。我不得不说你让我看到了事件驱动编程的潜力。