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

nicholish en  

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());
      
   }
  
}
nicholish en  

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;
Amir Yacoby  
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'.

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;
nicholish en  
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.
Amir Yacoby  
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.
nicholish en  
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 !
Amir Yacoby  
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.
nicholish en  
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.
Amir Yacoby  
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.