Comment surcharger Compare() dans CObject pour que CList sort() fonctionne ?

 

Je ne trouve pas de documentation sur la façon d'implémenter le tri des Listes dans mql5. Je vois que CList appelle la méthode Compare() depuis le pointeur CObject. Alors comment puis-je appeler la méthode surchargée de la classe enfant Compare() à partir du pointeur parent ?

Exemple :

#include <Arrays\List.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+

class PriceScore : public CObject
{
protected:
   int price;
   int score;
public:
                  PriceScore(void){}
                  PriceScore(int p, int s):price(p),score(s){}
                  ~PriceScore(void){}
   int            Compare(const CObject *node,const int mode=0);
   void           Price(const int p){price = p;}
   int            Price() const {return price;}
   void           Score(const int s){score = s;}
   int            Score() const {return score;}
  
};
int PriceScore::Compare(const CObject *node,const int mode=0) //Can't call this override from CList
{
   PriceScore *pc = (PriceScore*)node;
   Print(__FUNCTION__,":Compare called. Incoming: ",pc.Score()," This: ", score); //Doesn't log because this isn't called from CObject'
   if(pc.Score()< score)return 1;
   else if(pc.Score()> score) return -1;
   else return 0;
}

void OnStart()
  {
//---
   CList list;

   list.Add( new PriceScore(100,500));
   list.Add( new PriceScore(1,5));
   list.Add( new PriceScore(13,5000));
   list.Add( new PriceScore(987987,567));
   list.Add( new PriceScore(98798778,1));
  
   PriceScore *node = NULL;
   Print("-------------------",TimeCurrent(),"--------------------");
   for(int i=0;i<list.Total();i++)
   {
      node = list.GetNodeAtIndex(i);
      Print("Price = ",node.Price(),", Score = ",node.Score());
      
   }
   list.Sort(1); //Can't call overriden child method'
  
  
   Print("-------------------SORTED--------------------");
   for(int i=0;i<list.Total();i++)
   {
      node = list.GetNodeAtIndex(i);
      Print("Price = ",node.Price(),", Score = ",node.Score());
      
   }
  
}
 

J'ai trouvé la solution, mais je la laisse ici au cas où quelqu'un d'autre rencontrerait le même problème.

J'ai oublié de mettre le mot clé const après la méthode override qui a changé sa signature.

int            Compare(const CObject *node,const int mode=0);

int            Compare(const CObject *node,const int mode=0) const;
 
nicholishen:

J'ai trouvé la solution, mais je la laisse ici au cas où quelqu'un d'autre rencontrerait le même problème.

J'ai oublié de mettre le mot clé const après la méthode override qui a changé sa signature.

int            Compare(const CObject *node,const int mode=0);

int            Compare(const CObject *node,const int mode=0) const;

Pour cela, vous devez toujours utiliser le mot clé 'override' lorsque vous surchargez des méthodes, de cette façon le compilateur crie si la signature de la méthode est modifiée :

int            Compare(const CObject *node,const int mode=0) override const;

il ne compilera pas à cause de la différence 'const'.

 

Et vous avez également oublié le mot clé "virtuel" dans les deux cas :

virtual int            Compare(const CObject *node,const int mode=0) override const;
 
Amir Yacoby:

Et vous avez également oublié le mot clé "virtuel" dans les deux cas :

virtual int            Compare(const CObject *node,const int mode=0) override const;
Non... Je ne veux pas que le fils soit surchargé par un éventuel dérivé. J'ai oublié const pour le faire fonctionner, et override pour confirmer avec le compilateur.
 
nicholishen:
Non... Je ne veux pas que l'enfant soit surchargé par un éventuel dérivé. J'ai manqué const pour le faire fonctionner, et override pour confirmer avec le compilateur.
Oui, mais au moins dans CObject vous avez besoin du mot clé virtual.
 
Amir Yacoby:
Oui, mais au moins dans CObject tu as besoin du mot clé virtual.
J'ai compris... Je ne m'occupe pas des classes de base dans la bibliothèque et elle l'a par défaut, mais vous avez raison. Merci pour l'astuce de l'override !
 
nicholishen: Non... Je ne veux pas que le fils soit surchargé par une éventuelle classe dérivée.
  1. Ne pas ajouter le virtuel est une mauvaise pratique, mais n'est pas obligatoire (sauf dans le CObject.)
  2. Ne pas ajouter le virtuel ne change rien, il peut toujours être surchargé dans une classe dérivée.
  3. MT4/5 n'a pas de mot clé final.
 
whroeder1:
  1. Ne pas ajouter le virtuel est une mauvaise pratique, mais n'est pas obligatoire (sauf dans le CObject.)
  2. Ne pas ajouter le virtuel ne change rien, il peut toujours être surchargé dans une classe dérivée.
Vous avez tort ici, whroeder1.
Ne pas ajouter virtual dans la base vous fera perdre le polymorphisme - la méthode sera appelée statiquement et non dynamiquement au moment de l'exécution.

class a
{
public:
   void Sub()
     {
      Print("a.sub");
     }
};
class b : public a
{
public:
   void Sub()
     {
      Print("b.sub");
     }
};
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   clsa *a;
   clsa=new b;
   clsa.Sub();
  }
Si Sub dans la classe a n'a pas le virtual, alors tout pointeur de type a qui a une référence b actuelle n'appellera jamais la méthode b.Sub() au moment de l'exécution.
 
Amir Yacoby:
Vous avez tort ici, whroeder1.
Ne pas ajouter virtual dans la base vous fera perdre le polymorphisme - la méthode sera appelée statiquement et non dynamiquement au moment de l'exécution.

class a
{
public:
   void Sub()
     {
      Print("a.sub");
     }
};
class b : public a
{
public:
   void Sub()
     {
      Print("b.sub");
     }
};
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   a *clsa;
   clsa=new b;
   clsa.Sub();
  }
Si Sub dans la classe a n'a pas le virtual, alors tout pointeur de type a qui a une référence b actuelle n'appellera jamais la méthode b.Sub() au moment de l'exécution.
Correct. De même, omettre virtual signifie que la classe dérivée peut surcharger, mais ne sera pas appelée depuis un pointeur parent.
 
nicholishen:
Correct. De même, omettre virtual signifie que la classe dérivée peut être surchargée, mais ne sera pas appelée depuis un pointeur parent.
ce qui est exactement l'exemple que j'ai donné ( : a est le parent, et il appelle a.sub et non b.sub.
Raison: