¿Cómo anular Compare() en CObject para que funcione CList sort()?

 

No encuentro ninguna documentación sobre cómo implementar la ordenación de Listas en mql5. Veo que CList llama a Compare() desde el puntero CObject. Entonces, ¿cómo puedo llamar al método anulado Compare() de la clase hija desde el puntero padre?

Ejemplo:

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

Lo he resuelto, pero dejo aquí la solución por si alguien más se encuentra con el mismo problema.

Se me olvidó y la palabra clave const después del método override que cambió su firma.

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

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

Lo he resuelto, pero dejo aquí la solución por si alguien más se encuentra con el mismo problema.

Se me olvidó y la palabra clave const después del método override que cambió su firma.

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

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

para esto hay que usar la palabra clave 'override' siempre que se sobreescriben métodos, de esta manera el compilador grita si se cambia la firma del método:

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

no compilará debido a la diferencia de 'const'.

 

Y también has olvidado la palabra clave "virtual" en ambos casos:

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

Y también has olvidado la palabra clave "virtual" en ambos casos:

virtual int            Compare(const CObject *node,const int mode=0) override const;
No... no quiero que el hijo sea anulado por cualquier posible derivado. Me faltó const para que funcione, y override para confirmar con el compilador
 
nicholishen:
No... no quiero que el hijo sea anulado por cualquier posible derivado. Me faltó la const para que funcionara, y el override para confirmar con el compilador
Sí, pero al menos en CObject necesitas la palabra clave virtual
 
Amir Yacoby:
Sí, pero al menos en CObject se necesita la palabra clave virtual
Te entiendo... No me meto con las clases base de la librería y la tiene por defecto, pero tienes razón. ¡Gracias por el consejo del override!
 
nicholishen: No... no quiero que el hijo sea anulado por cualquier posible derivado.
  1. No añadir el virtual es una mala práctica, pero no es necesario (excepto en el CObject.)
  2. No añadir el virtual no cambia nada, todavía puede ser anulado en una clase derivada.
  3. MT4/5 no tiene una palabra clave final
 
whroeder1:
  1. No añadir el virtual es una mala práctica, pero no es necesario (excepto en el CObject.)
  2. No añadir el virtual no cambia nada, todavía puede ser anulado en una clase derivada.
Aquí te equivocas, whroeder1.
No añadir virtual en la base te hará perder el polimorfismo - el método será llamado estáticamente y no dinámicamente en tiempo de ejecución.

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 en la clase a no tiene el virtual, entonces cualquier puntero de tipo a que tenga una referencia real a b nunca llamará a b.Sub() en tiempo de ejecución.
 
Amir Yacoby:
Aquí te equivocas, whroeder1.
No añadir virtual en la base hará que pierdas el polimorfismo - el método será llamado estáticamente y no dinámicamente en tiempo de ejecución.

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();
  }
es Sub en la clase a no tiene el virtual, entonces cualquier puntero de tipo a que tenga una referencia real b nunca llamará al b.Sub() en tiempo de ejecución.
Es correcto. También omitir virtual significa que la clase derivada puede anular, pero no será llamada desde un puntero padre.
 
nicholishen:
Es correcto. También omitir virtual significa que la clase derivada puede anular, pero no será llamada desde un puntero padre.
que es exactamente el ejemplo que di (: a es el padre, y llama a.sub y no a b.sub.