como criar objetos de forma dinâmica? (Algumas coisas do OOP) - página 2

 
Doerk Hilger:

Eu não gostaria de dizer que você está fazendo isso totalmente errado, mas você faz, porque isto é programação estrutural e não OOP. A grande diferença é o poder da herança e a sobrecarga. E, a propósito, você não pode realmente herdar de objetos gráficos reais, mas você pode representar qualquer coisa como um objeto de código e se referir a uma linha ou qualquer coisa deste objeto. Esta é a maneira como normalmente é feito em qualquer classe, não importa se é uma classe MFC ou MQL, é a mesma coisa.

Se suas linhas são objetos, então trate-os como tal. Não lide com matrizes externas, faça-o dentro de uma coleção de classes e trabalhe com apontadores. Dê uma olhada no CWndContainer e tenha uma idéia do mesmo. Esta classe é um recipiente que gerencia principalmente matrizes de ponteiro para objetos CWnd. Dê um passo à frente, sua estrutura deve estar:

CObject como base para cada objeto

CPriceTimeObjects como base para cada objeto baseado em preço/tempo, tais como linhas, deriva de CObject. Ele controla a criação, detém tempo e preço e chama um OnCreate(), que pode ser usado pelo próximo herdeiro. Ele também tem uma função Tick que chama OnTick() virtual, que é então sobrecarregado pelos herdeiros.

CTrendLine como base para linhas de tendência, herda de CPriceTimeObjects e lida com OnCreate onde cria a linha final usando a função ObjectCreate. Também deve ter um manipulador OnTick() para reagir/responder em eventos Tick, pois deve ser sensível ao preço, tanto quanto eu entendi.

Além disto, você tem uma classe de contêineres que gerencia uma matriz de ponteiro que contém todos os objetos CTimePriceObject que você deseja, ela se herda também do CTimePriceObject e passa OnTick() para suas "crianças". O recipiente também tem uma função que manipula o OnChartEvent() para adicionar linhas ou para removê-las. Ele também deve ter uma função de escaneamento para escanear todos os objetos existentes para o caso, o especialista foi adicionado depois que as linhas foram criadas. Além disso, ele lida com o OnTick() sobrecarregado da CTimePrice, faz loops na matriz, pergunta a cada objeto CTrendLine nele se ele se sente de alguma forma responsável por reagir chamando a função Tick de cada objeto criança que é manuseado por um OnTick virtual. Por que isso novamente? Porque a CTrendLine sobrecarrega esta função também do CTimePrice e desta forma esta classe também pode ser herdada por outros herdeiros com outras funções.

Muito bem, Dorek.

Tenho algumas perguntas, no entanto:

Por que o recipiente que contém CTimePriceObject tem que herdar a si mesmo da CTimePriceObject? Por instinto, os CIndicators in standard lib herdam do CArrayObject e muito provavelmente se retransmitem ao CIndicator sem herdá-lo.
Sei que ele quer usar a lib padrão e não as arrays regulares, mas algo sobre o conceito de que um recipiente de objeto de certo tipo deve herdar o próprio objeto não está claro para mim (porque o recipiente não é em si mesmo um objeto que ele contém, ou seja, não há uma relação "é um"), mesmo que eu sinta que está certo.
Você pode esclarecer sua opinião sobre isso?

Em segundo lugar, você vê a construção de uma estrutura a partir do zero, sem depender da biblioteca padrão quando você diz que a CTrendLine cria a linha final usando a função ObjectCreate nativa da língua.
Quando você recomendaria estender as bibliotecas padrão em si, por exemplo, suponha que eu queira que todos os objetos CIndicator da biblioteca padrão sejam capazes de dizer quantas vezes o preço toca um buffer neles
nas últimas X barras. Como você faria isso, porque se você estender uma classe base como CIndicator, ou CIndicatorBuffer, então você terá todos os objetos dependentes da biblioteca padrão que têm que herdar de sua
novo CIndicator ou novo CBuffer, como por exemplo o CiMA. Você copiaria todas essas classes indicadoras personalizadas para sua pasta e mudaria sua herança para seu novo CIndicatorBuffer? e se as metaquotas acrescentassem algo a sua classe padrão CIndicatorBuffer ou acima?

Muito obrigado por seus insights.

BR

 

Willbur:

...

Quando você segue este caminho, meu objeto SmartLine deve aparecer no menu MT5 ao lado da linha de tendência, as setas, o objeto de texto e todas estas coisas.

...

Se o MT5 permitir isso, só então teremos que discutir a questão restante, como o objeto poderia ser acionado pelo programa terminal quando o preço mudar.

...
Não se pode fazer isso com mql5.
 
Amir Yacoby:

Muito bem, Dorek.

Tenho algumas perguntas, porém:

Por que o recipiente que contém o CTimePriceObject tem que herdar a si mesmo do CTimePriceObject? Por instinto, os CIndicators in standard lib herdam do CArrayObject e muito provavelmente se retransmitem ao CIndicator sem herdá-lo.
Sei que ele quer usar a lib padrão e não as arrays regulares, mas algo sobre o conceito de que um recipiente de objeto de certo tipo deve herdar o próprio objeto não está claro para mim (porque o recipiente não é em si mesmo um objeto que ele contém, ou seja, não há uma relação "é um"), mesmo que eu sinta que está certo.
Você pode esclarecer sua opinião sobre isso?

Em segundo lugar, você vê a construção de uma estrutura a partir do zero, sem depender da biblioteca padrão quando você diz que a CTrendLine cria a linha final usando a função ObjectCreate nativa da língua.
Quando você recomendaria estender as bibliotecas padrão em si, por exemplo, suponha que eu queira que todos os objetos CIndicator da biblioteca padrão sejam capazes de dizer quantas vezes o preço toca um buffer neles
nas últimas X barras. Como você faria isso, porque se você estender uma classe base como CIndicator, ou CIndicatorBuffer, então você terá todos os objetos dependentes da biblioteca padrão que têm que herdar de sua
novo CIndicator ou novo CBuffer, como por exemplo o CiMA. Você copiaria todas essas classes indicadoras personalizadas para sua pasta e mudaria sua herança para seu novo CIndicatorBuffer? e se as metaquotas acrescentassem algo a sua classe padrão CIndicatorBuffer ou acima?

Muito obrigado por seus insights.

BR

1. Herança do recipiente.

Uma limitação do MQL é que você não pode ter múltiplas heranças. Por causa disso, a pessoa tem que morrer pelo menos uma morte. Claro que você está certo, também faria sentido herdar do CArrayObj, mas eu não faria isso, porque o recipiente lida com todos os eventos que qualquer objeto CTimePrice lida, por exemplo o Tick()->OnTick() e os distribui para suas crianças, enquanto um objeto array não tem nada a ver com isso. Um objeto container que contém outros objetos que também herdam da mesma classe base, tem simplesmente mais pontos em comum. Tal recipiente também poderia ter uma âncora, com base em um preço e em um tempo, e quando você move/troca tal recipiente, seria a função do recipiente mover todas as suas "crianças" também. Este é apenas um exemplo, mas a lista de tais idéias é provavelmente mais longa do que a lista de idéias sobre as funcionalidades do array.

E sim, também é assim, que eu realmente criei tais classes que lidam com muitos tipos de tais objetos e a experiência que eu tenho enquanto isso me diz, que foi a decisão certa para gerenciar herdar desta maneira.

2. Estrutura a partir do zero.

Similar aqui. Quando comecei a ir um pouco mais fundo nas bibliotecas padrão, encontrei muitas coisas que não me agradaram. Não só o mau desempenho dessas, mas também a falta de flexibilidade e também por causa de uma arquitetura incompleta. Falta-me, por exemplo, uma classe/objeto CMouse global, bem como uma classe entre CWnd e CObject, porque os objetos CWnd são crianças do gráfico, assim como as linhas são, e não há nenhuma conexão com tais objetos e nenhuma implementação final de tais objetos, como descrevi acima. E, não há nenhum objeto mestre, que contenha todos esses objetos do gráfico, o que torna possível mostrar/ocultar todos eles com um único comando, destruí-los, o que quer que seja. CCanvas, a mesma coisa, uma boa classe, mas onde está a implementação com o CWnd que me permite criar objetos interativos baseados em bitmaps que herdam do CWnd? E assim por diante.

Além disso, toda a estrutura de todas as bibliotecas padrão não permite cadeias laterais, mas isto é necessário, porque o MQL não permite pontos nulos. Por exemplo, a MQL não permite cadeias laterais: Estou usando uma CDragLine classificada que me permite criar objetos de linha de tendência que podem ser arrastados. Se tal objeto da linha de tendência estiver conectado a uma ordem e, além disso, a um painel/exibição de qualquer coisa, preciso da possibilidade de usar uma cadeia lateral tal que o painel conectado também receba informações sobre movimentos/mudanças, que são causados por mudanças na ordem. E vice-versa, preciso da opção de mover a ordem e informar o painel quando a própria linha foi arrastada. Este tipo de mensagem triangular não pode ser feito com a biblioteca padrão. Isto é feito por cadeias laterais, o que significa, absolutamente todos os objetos herdados de uma versão avançada do CObject. Esta classe tem a possibilidade de conectar qualquer outro objeto a um objeto e de enviar mensagens a esse objeto conectado. Desta forma, é possível um envio de mensagens muito mais complexo e eficaz sem ter nenhum código esquisito.

Não posso dar nenhuma recomendação do que todos deveriam fazer, mas na verdade decidi abandonar 99% das bibliotecas padrão, as únicas classes que me restam dos originais são CCanvas (mas com algumas mudanças e correções de bugs, veja Code Base) e CSymbolInfo.

--------------

Se alguém estiver interessado na funcionalidade da cadeia lateral, aqui está o código da minha classe CObject. Se você substituir o CObject original no Object.mqh após cada atualização, a maioria das bibliotecas padrão obtém as melhorias desta funcionalidade de cadeia lateral. E, a propósito, a conexão do nó também é implementada - o que não é feito no original.

Para adicionar essa característica de cadeia lateral, faça-o da seguinte forma dentro da classe receptora:

//--- 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))
               {
               //...
               } 
         }            
   };

As classes de envio C WhatEver e CF, além disso, não sabem nada sobre o receptor, sobre se existe ou não um receptor. O código é apenas o seguinte:

//---
//...

   CustomEvent(123456);

//...
//---

Aqui está a substituição do 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
 

Obrigado por elaborar e por compartilhar a idéia da cadeia lateral.

Francamente, eu não entendo muito bem e o código não compila (criei o C WhatEver e o CF, além disso, como classes vazias herdando diretamente o CObjeto que você postou apenas com o construtor só para ter a idéia).

2 compilar erros recebidos sobre o CWhatEver::CWhatEver não pode chamar de função de membro privado e o mesmo erro para CFurther.

De qualquer forma, eu pesquisei no Google o termo "cadeia lateral" e não consegui encontrar. Se por acaso você souber de algum material escrito que eu possa ler, seria ótimo.

BR

 

Sim, as classes estão vazias, é por isso que não se compilam. Estes são apenas os titulares dos lugares.

As cadeias laterais podem não ser a descrição oficial para ele, mas sim o meu MQL limpo, que funciona como uma alternativa para os indicadores vazios e/ou herança múltipla. Em poucas palavras: Ela lhe dá a capacidade de se comunicar entre quaisquer classes e é muito útil se os objetos estiverem embutidos em classes, porque você pode "dizer" a eles que a classe que os contém é o receptor de seus eventos personalizados. Desta forma, você pode evitar qualquer codificação de spaghetti.

Por exemplo, se você tiver algo como

retornar OnClick();

que chamaria primeiro a sobrecarga da última classe derivante, você poderia estendê-la a algo como

retornar OnClick()&CustomEvent(CUSTOM_CLICK);

Desta forma, não apenas a classe derivante que normalmente tem um

bool virtual OnClick()

A função seria notificada, também a classe que inclui o objeto é capaz de receber a notificação por clique.

 

Você quer dizer que é uma forma de comunicação entre dois objetos quaisquer, não através de mensagens habituais que precisam conhecer o outro objeto para o qual está enviando uma mensagem, mas através de eventos personalizados?

Essa idéia eu tenho, mas o que você quer dizer com objetos embutidos? em seu código de CAnyClass você tinha dois objetos particulares. Você quer dizer com embutido que seus corpos também devem ser decalcados dentro
CAnyClass ou eles podem ser definidos fora? Quer dizer, embutido como você usa o termo é um objeto que é privado dentro de outro objeto?

Se, por exemplo, eu escrever CWhatEver fora da CAnyClass, e quiser enviar uma mensagem à CAnyClass sobre um evento, você quer dizer que em CWhatEver eu vou retornar

retornar OnClick()&CustomEvent(CUSTOM_CLICK); // <==== você quer dizer && em vez de & ????

que enviará uma mensagem à classe derivada de C WhatEver como de costume e também a classe que contém CAnyClass (por causa do evento personalizado)?

E por que CObject é definido dentro de um #ifndef?

BTW, apenas curioso, qual é a finalidade do stdlib.mqh em seu CObject?

 

De baixo para cima ...

- o stdlib, não está incluído com o objeto original.mqh? Eu não preciso dele aqui, mas ele contém CompareDouble() que é a única maneira confiável de comparar duas duplas

- Eu uso #ifndef para evitar definições duplas por padrão e para compilação condicional

- sim, para o envio de mensagens triangulares

- o binário & e a versão lógica && são o mesmo com resultados de bool

- por embutido quero dizer, se uma instância/objeto de uma classe estrangeira é parte de outra classe e ambas não derivam uma da outra. Use o exemplo OnClick() e assuma, o m_object manipula o OnClick() de alguma forma para fins internos, por exemplo, se é um botão ou o que quer que seja. Como seria o código se você precisasse saber dentro da CAnyClass que houve um clique sem este evento personalizado?

Para ser honesto, não é uma funcionalidade que se precisa todos os dias para todos os fins, mas se existe uma situação em que você precisa se comunicar não apenas direcional, é a solução.

Voltar à questão do tópico original. A idéia era usar matrizes para gerenciar múltiplas linhas que reagem ao preço. Estes objetos de linha lidam com eventos quando eles/elas são atravessados por uma vela e forçam alguma ação. Eu recomendei o uso de um contêiner. Se este recipiente agora quer contar estas ações, apenas por exemplo, pode ser facilmente feito desta forma quando o recipiente diz a cada objeto que ele é o receptor do evento personalizado, usando por exemplo

m_trendline[n].CustomEventReceiver(PTR(this));

enquanto a classe CTrendLine - é claro - tem que implementá-la de alguma forma como esta:

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED);

 
Uau, como é bom que haja uma discussão desse tipo no fórum. Embora eu deva admitir que ainda estou no início do OOP.

Infelizmente, eu vou
de férias por duas semanas. Oavião sai em horas (ok, poderia ser pior).

Uma pergunta que eu tenho neste momento: Existe em algum lugar uma documentação detalhada sobre o trabalho da moldura MQL?


Willbur
 
Willbur:
..

Uma pergunta que eu tenho neste momento: Existe em algum lugar uma documentação detalhada sobre o trabalho da moldura MQL?

Não :-(

A partir de minha experiência, é bom aprender a estudar a "estrutura mql". Mas, como disse Doerk, há muitos problemas com a Biblioteca Padrão e, na minha opinião, ela não é utilizável em projetos sérios e grandes.

 
Doerk Hilger:

De baixo para cima ...

- o stdlib, não está incluído com o objeto original.mqh? Eu não preciso dele aqui, mas ele contém CompareDouble() que é a única maneira confiável de comparar duas duplas

- Eu uso #ifndef para evitar definições duplas por padrão e para compilação condicional

- sim, para o envio de mensagens triangulares

- o binário & e a versão lógica && são o mesmo com resultados de bool

- por embutido quero dizer, se uma instância/objeto de uma classe estrangeira é parte de outra classe e ambas não derivam uma da outra. Use o exemplo OnClick() e assuma, o m_object manipula o OnClick() de alguma forma para fins internos, por exemplo, se é um botão ou o que quer que seja. Como seria o código se você precisasse saber dentro da CAnyClass que houve um clique sem este evento personalizado?

Para ser honesto, não é uma funcionalidade que se precisa todos os dias para todos os fins, mas se existe uma situação em que você precisa se comunicar não apenas direcional, é a solução.

Voltar à questão do tópico original. A idéia era usar matrizes para gerenciar múltiplas linhas que reagem ao preço. Estes objetos de linha lidam com eventos quando eles/elas são atravessados por uma vela e forçam alguma ação. Eu recomendei o uso de um contêiner. Se este recipiente agora quer contar estas ações, apenas por exemplo, pode ser facilmente feito desta forma quando o recipiente diz a cada objeto que ele é o receptor do evento personalizado, usando por exemplo

m_trendline[n].CustomEventReceiver(PTR(this));

enquanto a classe CTrendLine - é claro - tem que implementá-la de alguma forma como esta:

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED);

stdlib que eu pensava que era apenas MQL4, mas talvez eu esteja errado.
Desculpe, não entendi porque todo o CObject tem que estar dentro do #ifndef. O que será duplamente definido se for escrito de forma clara? E btw, por que você coloca o CStruct como classe vazia acima do CObject?

Sobre a classe embutida, (desculpe-me, sou programador há 25 anos também, mas não OO) seu exemplo se refere a uma situação como esta, por exemplo? Suponha que eu seja uma classe CCar, e eu tenho uma classe CWheel embutida.
E a roda CW tem um manipulador de eventos para pressão de ar mínima ou algo assim, e eu como o carro preciso saber disso?
Se você puder encontrar semelhanças com este exemplo ou outro específico, porque eu ainda não estou totalmente presente em termos de qual classe dispara eventos (no exemplo que você deu com um clique é obviamente o gráfico) e quem trata disso. E mesmo tecnicamente, entendendo por exemplo estas linhas (presumo que seu propósito é mostrar como o contêiner fará o trabalho de gerenciar as linhas de tendência que cruzam o preço por uma barra):

m_trendline[n].CustomEventReceiver(PTR(this));
você pode esclarecer se eu acertei? ele chama todas as linhas de tendência [1..n] em loop? se sim, que tipo de evento geral poderia ser que o contêiner está manipulando neste momento? não é linha
Pode ser como um novo bar ou uma mudança de preço que tem potencial para acionar o evento CTRENDLINE_CROSSED? E por que ele envia GetPointer(isto) para cada linha?
a linha tem que chamar o container de retorno, e por quê?
Então, se for esse o caso,

retornar OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED);
então cada linha chamará sua própria OnLineCrossed() - que deve ser um método regular que apenas verifica se o preço foi cruzado por aquela linha específica,
e depois chamar CustomEvent(CTRENDLINE_CROSSED) - que seu rolo é apenas para chamar ChartCustomEvent(...) para disparar o evento CTRENLINE_CROSSED - que em sua estrutura será tratado novamente no container? é essa última parte que faz a comunicação triangular ou talvez eu esteja apenas misturando aqui dois conceitos diferentes. Então o contêiner terá um manipulador CustomEvent que manipula CTRENDLINE_CROSSED?

escrevendo que algumas coisas ficaram mais claras eu acho (e o CCar pode ser desconsiderado se você quiser, e apenas focar na linha de tendência ) mas ainda não entendo por que mover GetPointer(isto) para cada linha? E em que tipo de evento, por exemplo, o container chamará o receptor de eventos personalizado em cada linha para detectar mudanças de preço, e onde está a comunicação triangular ou ela se aplica apenas ao exemplo anterior para apenas triangular (com CAnyClass)?
Você pode talvez aplicar o exemplo triangular nesta linha de tendência?

Agradeço muito o seu tempo e paciência e a sua ajuda até agora, não é nada óbvio. Devo dizer que você me abriu os olhos para o potencial da programação orientada a eventos.