как создавать объекты динамически? (Некоторые вопросы ООП) - страница 2

 
Doerk Hilger:

Мне не хотелось бы говорить, что вы делаете это совершенно неправильно, но это так, потому что это структурное программирование, а не ООП. Большая разница заключается в силе наследования и перегрузки. И кстати, вы не можете наследоваться от реальных графических объектов, но вы можете представить что угодно как объект кода и ссылаться на линию или что-то еще из этого объекта. Так обычно делается в любом классе, независимо от того, MFC это или MQL, все одинаково.

Если ваши строки являются объектами, то и обращайтесь с ними как с объектами. Не работайте с массивами снаружи, делайте это внутри класса коллекции и работайте с указателями. Посмотрите на CWndContainer и получите представление о нем. Этот класс представляет собой контейнер, который в основном управляет массивами указателей для объектов CWnd. Продвиньтесь на шаг вперед, ваша структура должна быть такой:

CObject в качестве базы для каждого объекта

CPriceTimeObjects как база для каждого объекта, основанного на цене/времени, например, линии, производного от CObject. Он контролирует создание, хранит время и цену и вызывает функцию OnCreate(), которая может быть использована следующим наследником. Он также имеет функцию Tick, которая вызывает виртуальную OnTick(), которая затем перегружается наследниками.

CTrendLine, как основа для линий тренда, наследуется от CPriceTimeObjects и обрабатывает OnCreate, где создает конечную линию, используя функцию ObjectCreate. Он также должен иметь обработчик OnTick() для реакции/отклика на события Tick, потому что он должен быть чувствителен к цене, насколько я понял.

Кроме этого, у вас есть класс-контейнер, который управляет массивом указателей, содержащим все объекты CTimePriceObject, которые вы хотите, он также наследуется от CTimePriceObject и передает OnTick() своим "детям". Контейнер имеет также функцию, которая обрабатывает OnChartEvent() для добавления линий или их удаления. Он также должен иметь функцию scan для сканирования всех существующих объектов на случай, если эксперт был добавлен после создания линий. Кроме того, она обрабатывает перегруженный OnTick() из CTimePrice, зацикливает массив, спрашивает каждый объект CTrendLine в нем, чувствует ли он себя каким-то образом ответственным, чтобы реагировать, вызывая функцию Tick каждого дочернего объекта, который обрабатывается виртуальным OnTick. Почему именно это? Потому что CTrendLine перегружает эту функцию также из CTimePrice и таким образом этот класс может быть унаследован другими наследниками с другими функциями.

Очень хорошо, Дорек.

Однако у меня есть несколько вопросов:

Почему контейнер, который содержит CTimePriceObject, должен наследоваться от CTimePriceObject? Например, CIndicators в стандартной lib наследует от CArrayObj и, скорее всего, ссылается на CIndicator, не наследуя его.
Я понимаю, что он хочет использовать стандартную либу, а не обычные массивы, но что-то в концепции, что контейнер объекта определенного вида должен наследовать сам объект, мне непонятно (потому что контейнер сам не является объектом, который он содержит, т.е. нет отношения "is a"), хотя я чувствую, что это правильно.
Не могли бы вы прояснить вашу точку зрения на это?

Во-вторых, вы считаете, что создаете фреймворк с нуля, не полагаясь на стандартную библиотеку, когда говорите, что CTrendLine создает конечную линию, используя родную для языка функцию ObjectCreate.
Когда вы рекомендуете расширять сами стандартные библиотеки, например, предположим, я хочу, чтобы все объекты CIndicator из стандартной библиотеки могли сказать, сколько раз цена касалась буфера в них
за последние X баров. Как бы вы поступили в этом случае, потому что если вы расширяете базовый класс, такой как CIndicator или CIndicatorBuffer, то у вас есть все зависимые объекты стандартной библиотеки, которые должны наследоваться от вашего
нового CIndicator или нового CBuffer, как, например, CiMA. Вы скопируете все эти пользовательские классы индикаторов в свою папку и измените их наследование на ваш новый CIndicatorBuffer? Что тогда, если metaquotes добавит что-то в свой стандартный CIndicatorBuffer или вышеуказанные классы?

Большое спасибо за понимание.

BR

 

Willbur:

...

Когда вы пройдете этот путь, мой объект SmartLine должен появиться в меню MT5 рядом с линией тренда, стрелками, текстовым объектом и всем остальным.

...

Если МТ5 это позволяет, то только тогда мы должны обсудить оставшийся вопрос, как объект может быть вызван программой терминала при изменении цены.

...
В mql5 этого сделать нельзя.
 
Amir Yacoby:

Очень хорошо, Дорек.

Однако у меня есть несколько вопросов:

Почему контейнер, который хранит CTimePriceObject, должен наследоваться от CTimePriceObject? Например, CIndicators в стандартной lib наследует от CArrayObj и, скорее всего, ссылается на CIndicator, не наследуя его.
Я понимаю, что он хочет использовать стандартную либу, а не обычные массивы, но что-то в концепции, что контейнер объекта определенного вида должен наследовать сам объект, мне непонятно (потому что контейнер сам не является объектом, который он содержит, т.е. нет отношения "is a"), хотя я чувствую, что это правильно.
Не могли бы вы прояснить вашу точку зрения на это?

Во-вторых, вы считаете, что создаете фреймворк с нуля, не полагаясь на стандартную библиотеку, когда говорите, что CTrendLine создает конечную линию, используя родную для языка функцию ObjectCreate.
Когда вы рекомендуете расширять сами стандартные библиотеки, например, предположим, я хочу, чтобы все объекты CIndicator из стандартной библиотеки могли сказать, сколько раз цена касалась буфера в них
за последние X баров. Как вы поступите в этом случае, ведь если вы расширите базовый класс типа CIndicator или CIndicatorBuffer, то у вас появятся все зависимые объекты стандартной библиотеки, которые должны будут наследоваться от вашего
нового CIndicator или нового CBuffer, как, например, CiMA. Вы скопируете все эти пользовательские классы индикаторов в свою папку и измените их наследование на ваш новый CIndicatorBuffer? Что тогда, если metaquotes добавит что-то в свой стандартный CIndicatorBuffer или вышеуказанные классы?

Большое спасибо за понимание.

BR

1. Наследование контейнера.

Ограничение MQL заключается в том, что вы не можете иметь несколько наследований. Из-за этого приходится умирать хотя бы одной смертью. Конечно, вы правы, было бы логично наследоваться от CArrayObj, но я бы не стал этого делать, потому что контейнер обрабатывает все события, которые обрабатывает любой объект CTimePrice, например, Tick()->OnTick() и распределяет их по своим дочерним объектам, а объект-массив не имеет к этому никакого отношения. Объект-контейнер, который содержит другие объекты, которые также наследуются от того же базового класса, просто имеют больше общих черт. Такой контейнер может также иметь якорь, основанный на цене и времени, и когда вы перемещаете/сдвигаете такой контейнер, он будет обязан перемещать и всех своих "детей". Это всего лишь пример, но список таких идей, вероятно, длиннее, чем список идей о функциональности массивов.

И да, именно так, я действительно создал такие классы, которые имеют дело со многими типами таких объектов, и опыт, который у меня есть, говорит мне, что это было правильное решение управлять наследованием таким образом.

2. Фреймворк с нуля.

Аналогично здесь. Когда я начал немного углубляться в стандартные библиотеки, я обнаружил много вещей, которые мне не понравились. Не только их плохая производительность, но и недостаточная гибкость, а также неполная архитектура. Мне не хватает, например, глобального класса/объекта CMouse, а также класса между CWnd и CObject, потому что объекты CWnd являются дочерними объектами графика, как и линии, и нет никакой связи с ними и никакой конечной реализации таких объектов, как я описал выше. И нет мастер-объекта, в котором хранятся все такие объекты графика, что позволяет показывать/скрывать их все одной командой, уничтожать их, что угодно. CCanvas, то же самое, хороший класс, но где реализация с CWnd, которая позволяет мне создавать интерактивные объекты на основе растровых изображений, которые наследуются от CWnd? И так далее.

Более того, вся структура всех стандартных библиотек не позволяет использовать боковые цепочки, но это необходимо, потому что MQL не позволяет использовать void-пойнтеры. Например: Я использую класс CDragLine, который позволяет мне создавать объекты линии тренда, которые можно перетаскивать. Если такой объект трендовой линии связан с ордером и, кроме того, с панелью/дисплеем, то мне нужна возможность использовать такую боковую цепочку, чтобы связанная панель получала информацию о движениях/изменениях, вызванных изменениями ордера. И наоборот, мне нужна возможность перемещать ордер и информировать панель о том, что сама линия была перетащена. Такой треугольный обмен сообщениями не может быть выполнен с помощью стандартной библиотеки. Это делается с помощью боковых цепочек, то есть абсолютно все объекты наследуются от расширенной версии CObject. Этот класс имеет возможность подключать любой другой объект к объекту и посылать сообщения такому подключенному объекту. Таким образом, становится возможным гораздо более сложный и эффективный обмен сообщениями без какого-либо странного кода.

Я не могу дать рекомендации, что делать всем, но на самом деле я решил отказаться от 99% стандартных библиотек, единственные классы, которые я оставил от оригиналов - это CCanvas (но с некоторыми изменениями и исправлениями, см. Code Base) и CSymbolInfo.

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

Если кого-то интересует функциональность боковой цепочки, вот код моего класса CObject. Если вы будете заменять оригинальный CObject в Object.mqh после каждого обновления, большинство частей стандартных библиотек получат улучшения этой функции боковой цепочки. И, кстати, соединение узлов тоже реализовано - чего не сделано в оригинале.

Чтобы добавить такую возможность боковой цепочки, сделайте это следующим образом внутри класса-получателя:

//--- 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.

В любом случае, я погуглил термин "side chain" и не смог найти. Если вы случайно знаете какой-нибудь письменный материал, который я могу прочитать, было бы здорово.

BR

 

Да, классы пустые, поэтому они не компилируются. Это просто заполнители.

Боковые цепочки, возможно, не являются официальным описанием для этого, это скорее мой чистый обходной путь MQL для недействительных указателей и/или множественного наследования. В двух словах: Это дает вам возможность общаться между любыми классами и очень полезно, если объекты встроены в классы, потому что вы можете "сказать" им, что содержащий класс является получателем их пользовательских событий. Таким образом, вы можете избежать любого спагетти-кодирования.

Например, если у вас есть что-то вроде

return OnClick();

которое сначала вызывает перегрузку последнего производного класса, вы можете расширить его до чего-то вроде

return OnClick()&CustomEvent(CUSTOM_CLICK);

Таким образом, не только производный класс, который обычно имеет

виртуальная функция bool OnClick()

будет уведомлен, но и класс, включающий объект, сможет получить уведомление о щелчке.

 

Вы имеете в виду, что это способ общения между любыми двумя объектами не с помощью обычного обмена сообщениями, который должен знать другой объект, которому он посылает сообщение, а с помощью пользовательских событий?

Эта идея мне понятна, но что вы имеете в виду под встроенными объектами? В вашем коде CAnyClass у вас есть два приватных объекта. Вы имеете в виду под embedded, что их тела должны быть также задекларированы внутри
CAnyClass или они могут быть определены снаружи? Я имею в виду, что встроенный, как вы используете этот термин, это объект, который является приватным внутри другого объекта?

Если, например, я пишу CWhatEver вне CAnyClass, и хочу сообщить CAnyClass о событии, вы имеете в виду, что в CWhatEver я верну

return OnClick()&CustomEvent(CUSTOM_CLICK); // <=== вы имеете в виду && вместо & ????

который, как обычно, сообщит производный класс CWhatEver, а также содержащий класс, который является CAnyClass (из-за пользовательского события)?

И почему CObject определен внутри #ifndef?

BTW, просто любопытно, каково назначение stdlib.mqh в вашем CObject?

 

Снизу вверх ...

- stdlib, разве она не включена в исходный object.mqh? Мне он здесь не нужен, но он содержит CompareDouble(), который является единственным надежным способом сравнения двух двоек.

- Я использую #ifndef, чтобы избежать двойных определений в стандарте и для условной компиляции.

- да, для треугольных сообщений

- двоичное & и логическая версия && одинаковы с результатом bool

- под встроенным я имею в виду, если экземпляр/объект постороннего класса является частью другого класса и оба не являются производными друг от друга. Используйте пример OnClick() и предположим, что объект m_object обрабатывает OnClick() как-то для внутренних целей, например, если это кнопка или что-то еще. Как будет выглядеть код, если вам нужно узнать в CAnyClass, что был клик без этого пользовательского события?

Честно говоря, это не та функциональность, которая нужна каждый день для любых целей, но если есть ситуация, когда вам нужно передавать информацию не только направленную, это решение.

Вернемся к первоначальному вопросу о потоке. Идея заключалась в использовании массивов для управления несколькими линиями, которые реагируют на цену. Эти линейные объекты обрабатывают события, когда их/их линии пересекаются свечой и заставляют действовать. Я рекомендовал использовать контейнер. Если этот контейнер теперь захочет подсчитывать эти действия, просто для примера, это можно легко сделать таким образом, когда контейнер сообщает каждому объекту, что он является получателем пользовательского события, например, с помощью

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

в то время как класс CTrendLine, конечно же, должен реализовать это как-то так:

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED);

 
Вау, как здорово, что на форуме есть такая дискуссия. Хотя я должен признать, что я все еще нахожусь в начале пути ООП.

К сожалению, я у
езжаю в отпуск на две недели.Самолет у летаетчерез несколько часов (ладно, могло быть и хуже).

Один вопрос у меня возник уже на этом этапе: Есть ли где-нибудь подробная документация по работе с MQL?


Willbur
 
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 лет, но не ОО) ваш пример относится к такой ситуации, например? Предположим, что у меня есть класс CCar, и я встроил в него класс CWheel.
И у CWheel есть обработчик события для минимального давления воздуха или что-то в этом роде, и мне, как автомобилю, нужно это знать?
Если вы можете найти сходство с этим примером или другие конкретные, потому что я все еще не совсем понимаю, какой класс запускает события (в примере, который вы привели с кликом, это, очевидно, диаграмма) и кто его обрабатывает. И даже технически, понимая, например, эти строки (я предполагаю, что их цель - показать, как контейнер будет выполнять работу по управлению линиями тренда, пересекающими цену на баре):

m_trendline[n].CustomEventReceiver(PTR(this));
Вы можете уточнить, правильно ли я понял? Он вызывает все линии тренда [1..n] в цикле? Если да, то какого типа общее событие может быть, которое контейнер обрабатывает прямо сейчас? Это не событие для конкретной линии, потому что он вызывает все линии.
Может ли это быть как новый бар или изменение цены, которое потенциально может вызвать событие CTRENDLINE_CROSSED? И почему он посылает GetPointer(this) каждой строке?
Должна ли линия иметь контейнер обратного вызова, и почему?
Итак, если это так,

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED);
тогда каждая линия будет вызывать свой собственный OnLineCrossed() - который должен быть обычным методом, который просто проверяет, была ли цена пересечена этой конкретной линией,
и затем вызовет CustomEvent(CTRENDLINE_CROSSED) - который будет просто вызывать ChartCustomEvent(...) для запуска события CTRENLINE_CROSSED - которое в вашей структуре будет обрабатываться снова в контейнере? Эта последняя часть делает треугольную связь или может быть я просто смешиваю здесь две разные концепции. То есть у контейнера будет обработчик CustomEvent, который будет обрабатывать CTRENDLINE_CROSSED?

Написав это, некоторые вещи стали более понятны, я думаю (и CCar можно игнорировать, если хотите, и просто сосредоточиться на линии тренда), но все же я не понимаю, зачем перемещать GetPointer(this) к каждой линии? И по какому событию, например, контейнер будет вызывать пользовательский приемник событий в каждой линии, чтобы обнаружить изменения цены, и где здесь треугольная связь или это только предыдущий пример только для треугольной (с CAnyClass)?
Можете ли вы применить пример с треугольником на этой линии тренда?

Я действительно благодарю вас за ваше время и терпение и помощь до сих пор, это совсем не очевидно. Должен сказать, что вы открыли мне глаза на потенциал событийно-управляемого программирования.