Comment créer des objets de manière dynamique ? (Quelques trucs de POO) - page 2

 
Doerk Hilger:

Je n'aime pas dire que vous le faites totalement mal, mais c'est le cas, car il s'agit de programmation structurée et non de POO. La grande différence est le pouvoir de l'héritage et de la surcharge. Et d'ailleurs, vous ne pouvez pas vraiment hériter d'objets graphiques réels, mais vous pouvez représenter n'importe quoi comme un objet de code et faire référence à une ligne ou autre à partir de cet objet. C'est la façon dont cela se fait habituellement dans n'importe quelle classe, que ce soit une classe MFC ou MQL, c'est la même chose.

Si vos lignes sont des objets, alors traitez-les comme tels. Ne traitez pas les tableaux à l'extérieur, faites-le dans une classe de collection et travaillez avec des pointeurs. Jetez un coup d'oeil à CWndContainer pour vous en faire une idée. Cette classe est un conteneur qui gère principalement les tableaux de pointeurs pour les objets CWnd. Allez un pas plus loin, votre structure devrait être :

CObject comme base pour chaque objet

CPriceTimeObjects comme base pour chaque objet basé sur le prix/le temps, comme les lignes, dérivé de CObject. Il contrôle la création, conserve le temps et le prix et appelle un OnCreate(), qui peut être utilisé par le prochain héritier. Il possède également une fonction Tick qui appelle un OnTick() virtuel, qui est ensuite surchargé par les héritiers.

CTrendLine comme base pour les lignes de tendance, hérite de CPriceTimeObjects et gère OnCreate où il crée la ligne finale en utilisant la fonction ObjectCreate. Il devrait également avoir un gestionnaire OnTick() pour réagir/répondre aux événements Tick, parce qu'il sera sensible au prix si j'ai bien compris.

En plus de cela, vous avez une classe conteneur qui gère un tableau de pointeurs qui contient tous les objets CTimePriceObject que vous voulez, il hérite lui-même aussi de CTimePriceObject et passe OnTick() à ses "enfants". Le conteneur possède également une fonction qui gère le OnChartEvent() pour ajouter ou supprimer des lignes. Il devrait également avoir une fonction de balayage pour analyser tous les objets existants dans le cas où l'expert a été ajouté après la création des lignes. De plus, elle gère le OnTick() surchargé de CTimePrice, boucle le tableau, demande à chaque objet CTrendLine s'il se sent responsable de réagir en appelant la fonction Tick de chaque objet enfant qui est géré par un OnTick virtuel. Pourquoi cela ? Parce que la CTrendLine surcharge cette fonction également à partir de CTimePrice et de cette façon, cette classe peut également être héritée par d'autres héritiers avec d'autres fonctions.

Très bien, Dorek.

J'ai cependant quelques questions :

Pourquoi le conteneur qui contient CTimePriceObject doit-il hériter lui-même de CTimePriceObject ? Par exemple, CIndicators dans la bibliothèque standard hérite de CArrayObj et fait très probablement référence à CIndicator sans en hériter.
Je sais qu'il veut utiliser la bibliothèque standard et non les tableaux ordinaires, mais quelque chose dans le concept selon lequel un conteneur d'objet d'un certain type devrait hériter de l'objet lui-même n'est pas clair pour moi (parce que le conteneur n'est pas lui-même un objet qu'il contient, c'est-à-dire qu'il n'y a pas de relation "est un") même si je pense que c'est juste.
Pouvez-vous clarifier votre point de vue à ce sujet ?

Deuxièmement, vous semblez construire le cadre de travail à partir de rien, sans vous appuyer sur la bibliothèque standard, lorsque vous dites que CTrendLine crée la ligne finale en utilisant la fonction ObjectCreate native du langage.
Quand recommanderiez-vous d'étendre les bibliothèques standard elles-mêmes, par exemple, supposons que je veuille que tous les objets CIndicator de la bibliothèque standard soient capables de dire combien de fois le prix a touché un tampon en eux
au cours des X dernières mesures. Comment procéder, car si vous étendez une classe de base comme CIndicator, ou CIndicatorBuffer, vous avez tous les objets dépendants de la bibliothèque standard qui doivent hériter de votre
nouveau CIndicator ou nouveau CBuffer, comme par exemple CiMA. Est-ce que vous copiez toutes ces classes d'indicateurs personnalisés dans votre dossier et changez leur héritage pour votre nouveau CIndicatorBuffer ? Que se passe-t-il alors si metaquotes ajoute quelque chose à son CIndicatorBuffer standard ou aux classes ci-dessus ?

Merci beaucoup pour vos commentaires.

BR

 

Willbur:

...

En procédant ainsi, mon objet SmartLine devrait apparaître dans le menu MT5 à côté de la ligne de tendance, des flèches, de l'objet texte et de tous ces éléments.

...

Si MT5 permet cela, alors seulement nous devons discuter de la question restante, comment l'objet pourrait être déclenché par le programme du terminal lorsque le prix change.

...
Vous ne pouvez pas faire cela avec MT5.
 
Amir Yacoby:

Très bien, Dorek.

J'ai quelques questions cependant :

Pourquoi le conteneur qui contient CTimePriceObject doit-il hériter lui-même de CTimePriceObject ? Par exemple, CIndicators dans la librairie standard hérite de CArrayObj et fait très probablement référence à CIndicator sans en hériter.
Je sais qu'il veut utiliser la bibliothèque standard et non les tableaux ordinaires, mais quelque chose dans le concept selon lequel un conteneur d'objet d'un certain type devrait hériter de l'objet lui-même n'est pas clair pour moi (parce que le conteneur n'est pas lui-même un objet qu'il contient, c'est-à-dire qu'il n'y a pas de relation "est un") même si je pense que c'est juste.
Pouvez-vous clarifier votre point de vue à ce sujet ?

Deuxièmement, vous semblez construire le cadre de travail à partir de rien, sans vous appuyer sur la bibliothèque standard, lorsque vous dites que CTrendLine crée la ligne finale en utilisant la fonction ObjectCreate native du langage.
Quand recommanderiez-vous d'étendre les bibliothèques standard elles-mêmes, par exemple, supposons que je veuille que tous les objets CIndicator de la bibliothèque standard soient capables de dire combien de fois le prix a touché un tampon en eux
au cours des X dernières mesures. Comment procéder, car si vous étendez une classe de base comme CIndicator, ou CIndicatorBuffer, vous avez tous les objets dépendants de la bibliothèque standard qui doivent hériter de votre
nouveau CIndicator ou nouveau CBuffer, comme par exemple CiMA. Est-ce que vous copiez toutes ces classes d'indicateurs personnalisés dans votre dossier et changez leur héritage pour votre nouveau CIndicatorBuffer ? Que se passe-t-il alors si metaquotes ajoute quelque chose à son CIndicatorBuffer standard ou aux classes ci-dessus ?

Merci beaucoup pour ces informations.

BR

1. Héritage du conteneur.

Une limitation de MQL est que vous ne pouvez pas avoir plusieurs héritages. Pour cette raison, on doit mourir au moins une fois. Bien sûr, vous avez raison, il serait également logique d'hériter de CArrayObj, mais je ne le ferais pas, parce que le conteneur gère tous les événements que tout objet CTimePrice gère, par exemple le Tick()->OnTick() et les distribue à ses enfants, alors qu'un objet tableau n'a rien à voir avec cela. Un objet conteneur qui contient d'autres objets qui héritent également de la même classe de base, a simplement plus de points communs. Un tel conteneur pourrait également avoir une ancre, basée sur un prix et une heure, et lorsque vous déplacez un tel conteneur, il aurait pour mission de déplacer tous ses "enfants" également. Ce n'est qu'un exemple, mais la liste de ces idées est probablement plus longue que celle des idées sur les fonctionnalités des tableaux.

Et oui, c'est aussi comme ça que j'ai réellement créé ces classes qui traitent de nombreux types d'objets et l'expérience que j'ai acquise entre-temps me dit que c'était la bonne décision de gérer l'héritage de cette manière.

2. Cadre de travail à partir de zéro.

Similaire ici. Quand j'ai commencé à aller un peu plus loin dans les bibliothèques standard, j'ai trouvé beaucoup de choses que je n'aimais pas. Non seulement les mauvaises performances de celles-ci, mais aussi le manque de flexibilité et aussi à cause d'une architecture incomplète. Il me manque par exemple une classe/objet global CMouse ainsi qu'une classe entre CWnd et CObject, car les objets CWnd sont des enfants du graphique, tout comme les lignes, et il n'y a pas de connexion à ces objets ni d'implémentation finale de ces objets comme je l'ai décrit ci-dessus. De plus, il n'y a pas d'objet maître qui contienne tous ces objets graphiques et qui permette de les afficher/masquer tous avec une seule commande, de les détruire ou autre. CCanvas, même chose, une belle classe mais où est l'implémentation avec CWnd qui me permet de créer des objets interactifs basés sur des bitmaps qui héritent de CWnd ? Et ainsi de suite.

De plus, la structure entière de toutes les bibliothèques standard ne permet pas les chaînes latérales, mais c'est nécessaire, car MQL ne permet pas les void-pointeurs. Par exemple : J'utilise une classe nommée CDragLine qui me permet de créer des objets ligne de tendance qui peuvent être glissés. Si un tel objet de ligne de tendance est connecté à un ordre et en plus à un panneau/affichage quelconque, j'ai besoin de la possibilité d'utiliser une telle chaîne latérale que le panneau connecté reçoive des informations sur les mouvements/changements aussi, qui sont causés par les changements de l'ordre. Et vice versa, j'ai besoin de la possibilité de déplacer l'ordre et d'informer le panneau lorsque la ligne elle-même a été déplacée. Ce type de messagerie triangulaire ne peut pas être réalisé avec la bibliothèque standard. Elle est réalisée par des chaînes latérales, ce qui signifie qu'absolument tous les objets héritent d'une version avancée de CObject. Cette classe a la possibilité de connecter n'importe quel autre objet à un objet, et d'envoyer des messages à un tel objet connecté. De cette façon, une messagerie beaucoup plus complexe et efficace est possible sans avoir de code bizarre.

Je ne peux pas donner de recommandation sur ce que tout le monde devrait faire, mais en fait j'ai décidé pour moi d'abandonner 99% des bibliothèques standards, les seules classes que j'ai laissées d'origine sont CCanvas (mais avec quelques changements et corrections de bugs, voir Code Base) et CSymbolInfo.

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

Si quelqu'un est intéressé par la fonctionnalité de la chaîne latérale, voici le code de ma classe CObject. Si vous remplacez la CObject originale dans Object.mqh après chaque mise à jour, la plupart des parties des bibliothèques standard bénéficient des améliorations de cette fonctionnalité de chaîne latérale. Et d'ailleurs, la connexion des nœuds est également implémentée - ce qui n'est pas fait dans l'original.

Pour ajouter une telle fonctionnalité de chaîne latérale, procédez comme suit à l'intérieur de la classe de réception :

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

Les classes d'envoi CWhatEver et CFurther ne savent rien sur le récepteur, qu'il y ait un récepteur ou non. Le code est juste le suivant :

//---
//...

   CustomEvent(123456);

//...
//---

Voici le remplacement de 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
 

Merci de développer et de partager l'idée de la chaîne latérale.

Franchement, je ne comprends pas bien, et le code ne compile pas (j'ai créé CWhatEver et CFurther comme des classes vides héritant directement du CObject que vous avez posté avec juste un constructeur juste pour avoir l'idée).

2 erreurs de compilation reçues à propos de CWhatEver::CWhatEver cannot call private member function et même erreur pour CFurther.

En tout cas, j'ai cherché sur Google le terme "side chain" et je n'ai rien trouvé. Si vous connaissez des documents que je peux lire, ce serait génial.

BR

 

Oui, les classes sont vides, c'est pourquoi elles ne compilent pas. Ce sont juste des placeholders.

Les chaînes latérales ne sont peut-être pas la description officielle, c'est plutôt mon contournement propre à MQL pour les pointeurs void et/ou l'héritage multiple. En quelques mots : Il vous donne la possibilité de communiquer entre n'importe quelles classes et est très utile si les objets sont intégrés dans des classes, parce que vous pouvez leur "dire" que la classe qui les contient est le récepteur de leurs événements personnalisés. De cette façon, vous pouvez éviter tout codage spaghetti.

Par exemple, si vous avez quelque chose comme

return OnClick() ;

qui appelle d'abord la surcharge de la dernière classe dérivée, vous pouvez l'étendre à quelque chose comme

return OnClick()&CustomEvent(CUSTOM_CLICK) ;

De cette façon, non seulement la classe dérivée qui a normalement une fonction

bool virtuelle OnClick()

sera notifiée, mais aussi la classe qui inclut l'objet pourra recevoir la notification de clic.

 

Voulez-vous dire que c'est un moyen de communiquer entre deux objets quelconques, non pas par la messagerie habituelle qui doit connaître l'autre objet auquel elle envoie un message, mais par des événements personnalisés?

Je comprends cette idée, mais qu'entendez-vous par objets intégrés ? Dans votre code de CAnyClass, vous avez deux objets privés. Voulez-vous dire par embedded que leurs corps doivent également être décalqués à l'intérieur de la
CAnyClass ou qu'ils peuvent être définis à l'extérieur ? Je veux dire que le terme "embedded", tel que vous l'utilisez, désigne un objet privé à l'intérieur d'un autre objet ?

Si par exemple j'écris CWhatEver à l'extérieur de CAnyClass, et que je veux envoyer un message à CAnyClass à propos d'un événement, voulez-vous dire que dans CWhatEver je renverrai

return OnClick()&CustomEvent(CUSTOM_CLICK) ; // <=== vous voulez dire && au lieu de & ? ???

ce qui enverra un message à la classe dérivée de CWhatEver comme d'habitude et aussi à la classe contenante qui est CAnyClass (à cause de l'événement personnalisé) ?

Et pourquoi CObject est défini à l'intérieur d'un #ifndef ?

BTW, juste par curiosité, quel est le but de stdlib.mqh dans votre CObject ?

 

De bas en haut ...

- la stdlib, n'est-elle pas de toute façon incluse avec le fichier original object.mqh ? Je n'en ai pas besoin ici, mais elle contient CompareDouble() qui est la seule façon fiable de comparer deux doubles.

- J'utilise #ifndef pour éviter les doubles définitions par défaut et pour la compilation conditionnelle

- oui, pour la messagerie triangulaire

- le & binaire et la version logique && sont les mêmes avec des résultats bools

- par embedded je veux dire, si une instance/objet d'une classe étrangère fait partie d'une autre classe et les deux ne dérivent pas l'un de l'autre. Utilisez l'exemple de OnClick() et supposez que l'objet m_object gère le OnClick() d'une manière ou d'une autre à des fins internes, par exemple s'il s'agit d'un bouton ou autre. A quoi ressemblerait le code si vous aviez besoin de savoir dans la CAnyClass qu'il y a eu un clic sans cet événement personnalisé?

Pour être honnête, ce n'est pas une fonctionnalité dont on a besoin tous les jours pour tous les usages, mais s'il y a une situation où vous avez besoin de communiquer non seulement la direction, c'est la solution.

Revenons au sujet du fil de discussion initial. L'idée était d'utiliser des tableaux pour gérer plusieurs lignes qui réagissent sur le prix. Ces objets lignes gèrent les événements lorsqu'elles/leurs lignes sont croisées par une bougie et forcent une action. J'ai recommandé d'utiliser un conteneur. Si ce conteneur veut maintenant compter ces actions, juste pour l'exemple, cela peut être facilement fait de cette façon quand le conteneur dit à chaque objet qu'il est le récepteur de l'événement personnalisé en utilisant par exemple

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

tandis que la classe CTrendLine - bien sûr - doit l'implémenter d'une manière ou d'une autre comme ceci :

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED) ;

 
Wow, c'est génial qu' il y ait une telle discussion dans le forum. Bien que je doive admettre que je suis encore au début de la POO.

Malheureusement, je pars
en vacances pour deux semaines. L'avion décolle dans quelques heures (ok, ça pourrait être pire).

J'ai déjàune question à ce stade: Existe-t-il quelque part une documentation détaillée sur le cadre MQL?


Willbur
 
Willbur:
..

J' ai déjàune question à ce stade: Existe-t-il une documentation détaillée sur le système MQL?

Non :-(

D'après mon expérience, il est bon d'apprendre à étudier le "framework mql". Mais comme l'a dit Doerk, il y a beaucoup de problèmes avec la bibliothèque standard, et à mon avis, elle n'est pas utilisable dans des projets sérieux et importants.

 
Doerk Hilger:

De bas en haut ...

- la stdlib, n'est-elle pas de toute façon incluse avec le fichier original object.mqh ? Je n'en ai pas besoin ici, mais elle contient CompareDouble() qui est la seule façon fiable de comparer deux doubles.

- J'utilise #ifndef pour éviter les doubles définitions par défaut et pour la compilation conditionnelle

- oui, pour la messagerie triangulaire

- le & binaire et la version logique && sont les mêmes avec des résultats bools

- par embedded je veux dire, si une instance/objet d'une classe étrangère fait partie d'une autre classe et les deux ne dérivent pas l'un de l'autre. Utilisez l'exemple de OnClick() et supposez que l'objet m_object gère le OnClick() d'une manière ou d'une autre à des fins internes, par exemple s'il s'agit d'un bouton ou autre. A quoi ressemblerait le code si vous aviez besoin de savoir dans la CAnyClass qu'il y a eu un clic sans cet événement personnalisé?

Pour être honnête, ce n'est pas une fonctionnalité dont on a besoin tous les jours pour tous les usages, mais s'il y a une situation où vous avez besoin de communiquer non seulement la direction, c'est la solution.

Revenons au sujet du fil de discussion initial. L'idée était d'utiliser des tableaux pour gérer plusieurs lignes qui réagissent sur le prix. Ces objets lignes gèrent les événements lorsqu'elles/leurs lignes sont croisées par une bougie et forcent une action. J'ai recommandé d'utiliser un conteneur. Si ce conteneur veut maintenant compter ces actions, juste pour l'exemple, cela peut être facilement fait de cette façon quand le conteneur dit à chaque objet qu'il est le récepteur de l'événement personnalisé en utilisant par exemple

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

tandis que la classe CTrendLine - bien sûr - doit l'implémenter d'une manière ou d'une autre comme ceci :

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED) ;

stdlib je pensais que c'était juste MQL4 mais peut-être que je me trompe.
Désolé, je n'ai pas compris pourquoi l'ensemble du CObject doit être dans #ifndef. Qu'est-ce qui serait doublement défini s'il était écrit en clair ? Et d'ailleurs, pourquoi mettre CStruct comme classe vide au-dessus de CObject ?

A propos de la classe embarquée, (désolé, je suis programmeur depuis 25 ans aussi mais pas OO) votre exemple se réfère à une situation comme celle-ci par exemple ? Supposons que je suis une classe CCar, et que j'ai intégré une classe CWheel.
Et que la CWheel a un gestionnaire d'événement pour la pression d'air minimum ou autre chose, et que moi, en tant que voiture, j'ai besoin de le savoir ?
Si vous pouvez trouver des similitudes avec cet exemple ou d'autres spécifiques, parce que je ne suis pas encore tout à fait là en termes de quelle classe déclenche les événements (dans l'exemple que vous avez donné avec le clic, c'est évidemment le graphique) et qui le gère. Et même techniquement, comprendre par exemple ces lignes (je suppose que leur but est de montrer comment le conteneur va faire le travail de gestion des lignes de tendance croisant le prix par une barre) :

m_trendline[n].CustomEventReceiver(PTR(this));
pouvez-vous clarifier si j'ai bien compris ? est-ce qu'il appelle toutes les lignes de tendance [1..n] dans une boucle ? si oui, quel type d'événement général pourrait-il être que le conteneur gère en ce moment ? ce n'est pas un événement spécifique à une ligne parce qu'il appelle toutes les lignes.
Peut-il s'agir d'une nouvelle barre ou d'un changement de prix qui pourrait déclencher l'événement CTRENDLINE_CROSSED ? Et pourquoi envoie-t-il GetPointer(this) à chaque ligne ?
La ligne doit-elle rappeler le conteneur, et pourquoi ?
Donc, si c'est le cas

return OnLineCrossed()&CustomEvent(CTRENDLINE_CROSSED);
alors chaque ligne appellera son propre OnLineCrossed() - qui devrait être une méthode régulière qui vérifie juste si le prix a été franchi par cette ligne spécifique,
et ensuite appeler CustomEvent(CTRENDLINE_CROSSED) - dont le rôle est juste d'appeler ChartCustomEvent(..) pour déclencher l'événement CTRENLINE_CROSSED - qui dans votre structure sera traité à nouveau dans le conteneur ? Est-ce que cette dernière partie fait de la communication triangulaire ou peut-être que je mélange ici deux concepts différents. Donc le conteneur aura un gestionnaire CustomEvent qui gère CTRENDLINE_CROSSED ?

En écrivant cela, certaines choses sont devenues plus claires je pense (et la CCar peut être ignorée si vous voulez, et juste se concentrer sur la ligne de tendance) mais je ne comprends toujours pas pourquoi déplacer GetPointer(this) à chaque ligne ? Et sur quel type d'événement par exemple le conteneur va appeler le récepteur d'événement personnalisé dans chaque ligne afin de détecter les changements de prix, et où est la communication triangulaire ou est-ce qu'il s'applique seulement l'exemple précédent pour le triangulaire seulement (avec CAnyClass) ?
Pouvez-vous peut-être appliquer l'exemple triangulaire sur cette ligne de tendance ?

Je vous remercie vraiment pour votre temps, votre patience et votre aide jusqu'à présent, ce n'est pas évident du tout. Je dois dire que vous m'avez ouvert les yeux sur le potentiel de la programmation événementielle.