Come sovrascrivere Compare() in CObject in modo che CList sort() funzioni?

 

Non riesco a trovare alcuna documentazione su come implementare l'ordinamento delle liste in mql5. Vedo che CList chiama il Compare() dal puntatore CObject. Quindi come posso chiamare il metodo overridden della classe figlio Compare() dal puntatore genitore?

Esempio:

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

L'ho capito, ma lascio qui la soluzione nel caso in cui qualcun altro si imbatta nello stesso problema.

Ho dimenticato di e la parola chiave const dopo l'override del metodo che ha cambiato la sua firma.

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

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

L'ho capito, ma lascio qui la soluzione nel caso in cui qualcun altro si imbatta nello stesso problema.

Ho dimenticato di e la parola chiave const dopo l'override del metodo che ha cambiato la sua firma.

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

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

Per questo dovete usare la parola chiave 'override' sempre quando sovrascrivete i metodi, in questo modo il compilatore grida se la firma del metodo è cambiata:

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

non compilerà a causa della differenza 'const'.

 

E hai anche dimenticato la parola chiave 'virtuale' in entrambi i casi:

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

E hai anche dimenticato la parola chiave 'virtuale' in entrambi i casi:

virtual int            Compare(const CObject *node,const int mode=0) override const;
No... non voglio che il figlio sia sovrascritto da ogni possibile derivato. Mi mancava const per farlo funzionare, e override per confermare con il compilatore
 
nicholishen:
No... non voglio che il bambino sia sovrascritto da ogni possibile derivato. Mi mancava const per farlo funzionare, e override per confermare con il compilatore
Sì, ma almeno in CObject hai bisogno della parola chiave virtuale
 
Amir Yacoby:
Sì, ma almeno in CObject hai bisogno della parola chiave virtuale
Ho capito... Non faccio casino con le classi base della libreria e ce l'ha di default, ma hai ragione. Grazie per la dritta sull'override!
 
nicholishen: No... non voglio che il figlio sia sovrascritto da ogni possibile derivato.
  1. Non aggiungere il virtuale è una cattiva pratica, ma non è richiesto (tranne che nel CObject).
  2. Non aggiungere il virtuale non cambia nulla, può ancora essere sovrascritto in una classe derivata.
  3. MT4/5 non ha una parola chiave finale
 
whroeder1:
  1. Non aggiungere il virtuale è una cattiva pratica, ma non è richiesto (tranne che nel CObject).
  2. Non aggiungere il virtuale non cambia nulla, può ancora essere sovrascritto in una classe derivata.
Qui ti sbagli, whroeder1.
Non aggiungere virtual nella base vi farà perdere il polimorfismo - il metodo sarà chiamato staticamente e non dinamicamente a tempo di esecuzione.

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();
  }
se Sub nella classe a non ha il virtuale, allora qualsiasi puntatore di tipo a che ha un riferimento b effettivo non chiamerà mai il b.Sub() a run time.
 
Amir Yacoby:
Qui ti sbagli, whroeder1.
Non aggiungere il virtuale nella base ti farà perdere il polimorfismo - il metodo sarà chiamato staticamente e non dinamicamente a tempo di esecuzione.

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();
  }
è Sub nella classe a non ha il virtuale, allora qualsiasi puntatore di tipo a che ha un riferimento b effettivo non chiamerà mai il b.Sub() a run time.
Corretto. Anche omettere il virtuale significa che la classe derivata può sovrascrivere, ma non sarà chiamata da un puntatore genitore.
 
nicholishen:
Corretto. Anche omettere virtuale significa che la classe derivata può sovrascrivere, ma non sarà chiamata da un puntatore genitore.
che è esattamente l'esempio che ho dato (: a è il genitore, e chiama a.sub e non b.sub.