CObjectでCompare()をオーバーライドして、CList sort()を動作させるには?

 

mql5でListのソートを実装する方法について、ドキュメントが見当たりません。CListは CObjectポインタからCompare()を呼び出して いますね。では、親ポインタから子クラスの Compare() オーバーライド・メソッドを呼び出すにはどうすればよいのでしょうか?

例を挙げます。

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

私はそれを理解しましたが、他の誰かが同じ問題に遭遇した場合のために、ここに解決策を残しておきます。

私は、シグネチャを変更したメソッドのオーバーライドの後に、キーワードconstを忘れていました。

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

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

私はそれを理解しましたが、他の誰かが同じ問題に遭遇した場合のために、ここに解決策を残しておきます。

私は、シグネチャを変更したメソッドのオーバーライドの後に、キーワードconstを忘れていました。

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

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

このため、メソッドをオーバーライドするときには常にキーワード 'override' を使用する必要があります。

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

const'の違いのためにコンパイルできません。

 

また、どちらの場合も「バーチャル」というキーワードを忘れていますね。

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

また、どちらの場合も「バーチャル」というキーワードを忘れていますね。

virtual int            Compare(const CObject *node,const int mode=0) override const;
いや、子プロセスが派生してオーバーライドされるのは困るんだ。コンストラクタで動作するようにし、オーバーライドでコンパイラに確認しました。
 
nicholishen:
いや・・・派生する可能性のある子にはオーバーライドさせたくないんです。コンストラクタで動作するようにし、コンパイラで確認するためにオーバーライドを見落とした
しかし、少なくともCObject では virtual キーワードが必要です。
 
Amir Yacoby:
しかし、少なくとも CObject では、virtual キーワードが必要です。
そうなんだ・・・。私はライブラリのベースクラスをいじらないので、デフォルトで持っていますが、あなたの言うとおりです。オーバーライドのヒントありがとうございました
 
nicholishen: いや・・・子クラスが派生しうるものからオーバーライドされるのは困る。
  1. virtualを 追加しないのは悪い習慣ですが、必須ではありません(CObjectを 除く)。
  2. virtualを 追加しなくても、派生クラスでオーバーライドすることは可能です。
  3. MT4/5にはfinal キーワードがない
 
whroeder1:
  1. virtualを 追加しないのは悪い習慣ですが、必須ではありません(CObjectを除く)。
  2. virtualを 追加しなくても、派生クラスでオーバーライドすることは可能です。
whroeder1 さん、あなたは間違っています。
基底クラスで virtual を追加しないと、ポリモーフィズムを失うことになります。

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();
  }
もし a クラスの Sub が virtual を持たない場合、実際の b を参照する a タイプのポインタは、実行時に b.Sub() を決して呼び出すことはありません。
 
Amir Yacoby:
whroeder1 さん、あなたは間違っています。
メソッドは静的に呼び出され、実行時に動的に呼び出されることはないのです。

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();
  }
aクラスのSubがvirtualを持たない場合、実際のbを参照するa型のポインタは、実行時にb.Sub()を決して呼び出すことはない。
正しいです。また、virtualを省略すると、派生クラスはオーバーライドできるが、親ポインタから呼び出されることはない。
 
nicholishen:
その通りです。また、virtualを省略すると、派生クラスはオーバーライドできますが、親ポインタから呼び出されることはありません。
というのは、まさに私が挙げた例です(: a が親で、a.sub を呼び出し、b.sub は呼び出さない。