如何动态地创建对象?(一些OOP的东西) - 页 2

 
Doerk Hilger:

我不想说你的做法是完全错误的,但你确实这样做了,因为这是结构编程,而不是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允许这样做,那么我们就必须讨论剩下的问题,即当价格变化时,终端程序如何触发该对象。

...
你不能用mql5做这个。
 
Amir Yacoby:

非常好,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,标准库的大部分部分就会得到这个侧链功能的增强。顺便说一下,节点连接也被实现了--这在原版中是没有做到的。

要添加这样的侧链功能,请在接收类里面做如下工作。

//--- 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                    |
   //+------------------------------------------------------------------+
      virtual void      OnCustomEvent(CObject * sender, int eventid)
         {
            if (sender==PTR(m_object))
               {
               switch (eventid)
                  {
                  case 123456:
                     Print("Here we go with 123456");
                     break;
               //...
                  }
               }
            else if (sender==PTR(m_further))
               {
               //...
               } 
         }            
   };

发送类CWhatEver和CFurther不知道任何关于接收者的信息,不知道是否有接收者。代码就是如下。

//---
//...

   CustomEvent(123456);

//...
//---

这里是CObject的替换。

//+------------------------------------------------------------------+
//|                                                       Object.mqh |
//|                                               Copyright by Doerk |
//+------------------------------------------------------------------+

#ifndef __DH_OBJECT_CLASS
#define __DH_OBJECT_CLASS

#include <stdlib.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//| Defintions                                                       |
//|                                                                  |
//+------------------------------------------------------------------+
#ifndef  PTR
   #define  PTR(object) GetPointer(object)
   #define  PTR_DELETE(object) { if (CheckPointer(object)==POINTER_DYNAMIC) delete object; }
   #define  PTR_INVALID(object) (object==NULL || CheckPointer(object)==POINTER_INVALID)
#endif   

enum ENUM_FILE_IO
   {
   FILE_IO_NONE = 0,    //--- No file interaction
   FILE_IO_BINARY = 1,  //--- Binary, OnLoad() / OnSave() events
   FILE_IO_INI = 2,     //--- Ini file, OnLoadIni() / OnSaveIni() events
   };
   
//+------------------------------------------------------------------+
//|                                                                  |
//| Class CStruct - the base of everything                           |
//|                                                                  |
//+------------------------------------------------------------------+
class CStruct
   {
   };
//+------------------------------------------------------------------+
//|                                                                  |
//| Class CObject                                                    |
//|                                                                  |
//+------------------------------------------------------------------+
class CObject : public CStruct
  {
   protected:      
      long              m_obj_id;               //--- Unique ID of each object
      string            m_classname;            //--- Name of (deriving) class
      CObject          *m_prev;                 //--- Previous item in chain
      CObject          *m_next;                 //--- Next item in chain
      CStruct          *m_struct;               //--- Additional attached struct
      CObject          *m_object;               //--- Additional attached object
      string            m_tag;                  //--- Additional tag (File operation)
   private:
      CObject          *m_eventreceiver;        //--- Object which gets custom notifications
      ENUM_FILE_IO      m_fileio;               //--- Enables/disable file input/output
      
   public:
      //+------------------------------------------------------------------+
      //| Construction                                                     |
      //+------------------------------------------------------------------+
      CObject(void) : m_fileio(FILE_IO_BINARY),
                      m_struct(NULL),
                      m_tag(NULL),
                      m_object(NULL)
         {
      //--- Set ID
            __obj_cnt++;
            m_obj_id=__obj_cnt;
      //--- Reset notified object            
            m_eventreceiver=NULL;
      //--- Connect chain            
            Prev(__obj_prev);
            if (__obj_prev!=NULL)
               __obj_prev.Next(PTR(this));
            __obj_prev=PTR(this);
            Next(NULL);
            
         } 
         
      //+------------------------------------------------------------------+
      //| Destruction                                                      |
      //+------------------------------------------------------------------+
      ~CObject(void)
         {
      //--- Reconnect chain
            if (m_prev!=NULL)
               m_prev.Next(m_next);
            if (m_next!=NULL)
               m_next.Prev(m_prev);    
            if (__obj_prev==PTR(this))
               __obj_prev=Prev();                    
         } 
      //+------------------------------------------------------------------+
      //| Chain access                                                     |
      //+------------------------------------------------------------------+
   public:      
      CObject          *Prev(void)                                      const { return(m_prev); }
      void              Prev(CObject *node)                                   { m_prev=node;    }
      CObject          *Next(void)                                      const { return(m_next); }
      void              Next(CObject *node)                                   { m_next=node;    }
      //+------------------------------------------------------------------+
      //| Custom events - allows interaction between embedded objects and  |
      //|                containers                                        |
      //+------------------------------------------------------------------+
   public:
      CObject *         CustomEventReceiver(void)                       const { return(m_eventreceiver); }
      bool              CustomEventReceiver(CObject *receiver)
         {
            if (m_eventreceiver!=NULL)
               return false;
            m_eventreceiver=receiver;
            return true;   
         }
      void              CustomEvent(int eventid=0)
         {
            if (!PTR_INVALID(m_eventreceiver))
               m_eventreceiver._CustomEvent(PTR(this), eventid);
         }      
      void              _CustomEvent(CObject * sender, int eventid)
         {
            OnCustomEvent(sender, eventid);
         }      
   protected:
      virtual void      OnCustomEvent(CObject * sender, int eventid)         
         {
         }
                                          
      //+------------------------------------------------------------------+
      //| File interaction                                                 |
      //+------------------------------------------------------------------+
   public:
      bool              Save(const int file_handle)                           { if (m_fileio==FILE_IO_NONE) return true; return(OnSave(file_handle));   }
      bool              Load(const int file_handle)                           { if (m_fileio==FILE_IO_NONE) return true; return(OnLoad(file_handle));   }
      bool              Save(CObject *fileobject)                             { if (m_fileio==FILE_IO_NONE) return true; return(OnSave(fileobject)); }
      bool              Load(CObject *fileobject)                             { if (m_fileio==FILE_IO_NONE) return true; return(OnLoad(fileobject)); }
      bool              LoadDefault(void)                                     { return (OnLoadDefault()); }
      bool              FileIO(const ENUM_FILE_IO flag)                       { m_fileio=flag; return true; }
      ENUM_FILE_IO      FileIO(void)                                          { return m_fileio; }
   protected:
      virtual bool      OnSave(const int file_handle)                         { return true; }
      virtual bool      OnLoad(const int file_handle)                         { return true; }
      virtual bool      OnSave(CObject *fileobject)                           { return true; }
      virtual bool      OnLoad(CObject *fileobject)                           { return true; }
      virtual bool      OnLoadDefault(void)                                   { return true; }
      
      //+------------------------------------------------------------------+
      //| Identification                                                   |
      //+------------------------------------------------------------------+
   public:      
      long              Id(void)                                        const { return m_obj_id;    }
      virtual int       Type(void)                                      const { return(0);      }
      string            ClassName(void)                                 const { return(m_classname); }
      string            Tag(void)                                       const { return m_tag; }
      bool              Tag(string value)                                     { m_tag=value; return true; }

      //+------------------------------------------------------------------+
      //| Comparison                                                       |
      //+------------------------------------------------------------------+
   public:      
      virtual int       Compare(const CObject *node,const int mode=0)   const { return(0);      }
      
  };
//+------------------------------------------------------------------+
long __obj_cnt=-1;
CObject * __obj_prev=NULL;
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+

#endif                  // __DH_OBJECT_CLASS
 

谢谢你的阐述和分享侧链的想法。

坦率地说,我不太明白,而且代码不能编译(我把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)。

 
在论坛上 有这样的 讨论 真是太好了 虽然我 必须 承认, 仍然 处于 OOP 初级阶段

不幸的是, 我去 度假
两个星期。飞机 几小时 后起飞好吧 可能会更糟

这一点上 已经 一个 问题 有什么地方关于 MQL 框架 工作详细文件


威尔伯
 
Willbur:
..

这一点上 已经 一个 问题。是否 关于 MQL 框架 工作详细文件

没有 :-(

根据我的经验,学习研究 "mql框架 "是很好的。但正如Doerk所说,标准库有 很多问题,在我看来,它不能用于严肃的大项目。

 
Doerk Hilger:

从下到上...

- 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)?
你能在这个趋势线上应用三角形的例子吗?

我真的很感谢你的时间、耐心和迄今为止的帮助,这一点都不明显。我不得不说你让我看到了事件驱动编程的潜力。