Вопросы по ООП в MQL5 - страница 98

 
Alexey Viktorov #:

Что я делаю не правильно?

  virtual int        Type(voidoverride { return(0x7878); }// в CArrayObj — virtual int Type(void) const { return(0x7778); }

Если бы прописали override, то узнали бы сразу во время компиляции, что ошиблись с сигнатурой виртуального метода.

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Ошибки, баги, вопросы

fxsaber, 2025.02.10 15:55

Ну и слово override, но здесь уже требуется знание ООП и опыт обжиганий в виде долгого поиска слома выполнения кода. А если бы использовался (там, где работает и без него) override, то не было бы долгого поиска, а сразу бы стало понятна причина, что произошло.

 
fxsaber #:

Если бы прописали override, то узнали бы сразу во время компиляции, что ошиблись с сигнатурой виртуального метода.

Да, согласен. Это упущено при многократных изменениях кода.

Разница в том, что при правильном написании

  virtual int        Type(void) const  { return(0x7878); }// в CArrayObj — virtual int Type(void) const { return(0x7778); }

При записи перегруженная функция срабатывает, а при чтении нифига. Или наоборот, при записи не работает… Факт тот, что полностью не получается.

Там в коде ещё забаненые строки… Это неудавшиеся эксперименты.

    //CArrayObj::Save(handle);
    list.Save(handle);
    CArrayObj::Load(handle);
    //list.Load(handle);

А если при чтении писать list.Load(handle); то не отрабатывает виртуальная 

  virtual bool       CreateElement(const int index);

Моей фантазии для экспериментов уже не хватает. А знаний никогда и не было…

 
Alexey Viktorov #:

Моей фантазии для экспериментов уже не хватает. А знаний никогда и не было…

Никогда не использовал CObject, поэтому для ответа на вопрос посмотрел исходник и сразу все понял.

Был уверен, что должны быть примеры использования. Но поиском не нашел.


Тот случай, когда Документация (глянул после исходников) дает понимания много меньше, чем чтение самих исходников.

В общем, 100% нужен пример-исходник в данном контексте Load/Save, чтобы стало понятнее ООП-новичку. Тут объяснять - дороже будет гораздо.

 
Alexey Viktorov #:

Да, согласен. Это упущено при многократных изменениях кода.

Разница в том, что при правильном написании

При записи перегруженная функция срабатывает, а при чтении нифига. Или наоборот, при записи не работает… Факт тот, что полностью не получается.

Там в коде ещё забаненые строки… Это неудавшиеся эксперименты.

А если при чтении писать list.Load(handle); то не отрабатывает виртуальная 

Моей фантазии для экспериментов уже не хватает. А знаний никогда и не было…

Унаследуйся от CArrayObj и переопредели там метод CreateElement, чтобы он не возвращал NULL, а создавал нужный тебе тип объекта.
 
Artyom Trishkin #:
Унаследуйся от CArrayObj и переопредели там метод CreateElement, чтобы он не возвращал NULL, а создавал нужный тебе тип объекта.

Так и унаследован от 

class CTestClass : public CArrayObj

Вот только понять не могу почему, в каком-то случае Type() не виртуалится, а в каком-то CreateElement() ………

 
fxsaber #:

В общем, 100% нужен пример-исходник в данном контексте Load/Save, чтобы стало понятнее ООП-новичку. Тут объяснять - дороже будет гораздо.

Ниже рабочий пример, где подсветил виртуальные функции.

// Демонстрация записи/чтения CArrayObj на примере истории торговли.

// Структура сделки (пример).
struct DEAL
{
  ulong Ticket;
  datetime Time;
  string Symb;
  double Profit;
  
  void Set( const ulong lTicket )
  {
    this.Ticket = lTicket;
    
    this.Time = (datetime)::HistoryDealGetInteger(this.Ticket, DEAL_TIME);
    this.Symb = ::HistoryDealGetString(this.Ticket, DEAL_SYMBOL);
    this.Profit = ::HistoryDealGetDouble(this.Ticket, DEAL_PROFIT);
  }

  bool Save( const int Handle ) const
  {
    ::FileWriteLong(Handle, this.Ticket);    
    ::FileWriteInteger(Handle, (int)this.Time);    

    ::FileWriteInteger(Handle, ::StringLen(this.Symb));
    ::FileWriteString(Handle, this.Symb);
    
    ::FileWriteDouble(Handle, this.Profit);
        
    return(true);
  }
  
  bool Load( const int Handle )
  {
    this.Ticket = ::FileReadLong(Handle);
    this.Time = ::FileReadInteger(Handle);
    
    this.Symb = ::FileReadString(Handle, ::FileReadInteger(Handle));
    
    this.Profit = ::FileReadDouble(Handle);
    
    return(true);
  }  
};

#include <Arrays\ArrayObj.mqh>

class CDeal : public CObject
{
protected:
  DEAL Deal;
  
public:    
  virtual bool Save( const int file_handle ) override { return(this.Deal.Save(file_handle)); }  
  virtual bool Load( const int file_handle ) override { return(this.Deal.Load(file_handle)); }
  
  // Заполняет историей.
  CDeal* Set( const ulong lTicket ) { this.Deal.Set(lTicket); return(&this); }
    
  DEAL Get() const { return(this.Deal); }
};

class CDeals : public CArrayObj
{
public:  
  virtual bool CreateElement( const int index ) override return(::CheckPointer(this.m_data[index] = new CDeal) == POINTER_DYNAMIC); }
  
  // Распечатывает себя.
  void Print() const
  {
    DEAL Array[];
    
    for (uint i = ::ArrayResize(Array, this.Total()); (bool)i--;)
      Array[i] = dynamic_cast<CDeal*>(this.At(i)).Get();
      
    ::ArrayPrint(Array);
  }
  
  // Заполняет историей.
  int Set()
  {
    const int Size = ::HistoryDealsTotal();

    for (int i = 0; i < Size; i++)
      this.Add((new CDeal).Set(::HistoryDealGetTicket(i)));
    
    return(Size);
  }
};

void OnStart()
{
  const int Handle1 = FileOpen(__FILE__, FILE_WRITE | FILE_BIN); // Запись.
  
  if (Handle1 != INVALID_HANDLE)
  {    
    if (HistorySelect(0, INT_MAX))
    {
      CDeals Deals;
      
      Deals.Set(); // Заполнили историей.
      
      Deals.Save(Handle1); // Сохранили.
    }
    
    FileClose(Handle1);
  }

  const int Handle2 = FileOpen(__FILE__, FILE_READ | FILE_BIN); // Чтение.

  if (Handle2 != INVALID_HANDLE)
  {
    CDeals Deals;
    
    if (Deals.Load(Handle2)) // Прочитали.
      Deals.Print(); // Распечатали.
    
    FileClose(Handle2);
  }    
}


Скрипт считывает историю в CArrayObj-объект, записывает ее в файл. Затем считывает ее из файла и распечатывает (для проверки корректности).

 
fxsaber #:

Ниже рабочий пример, где подсветил виртуальные функции.


Скрипт считывает историю в CArrayObj-объект, записывает ее в файл. Затем считывает ее из файла и распечатывает (для проверки корректности).

Спасибо. Но как и все ваши коды читать катастрофически сложно.

И всё-же виртуальная функция

   virtual int       Type(void) const { return(0x7778); }

в вашем коде используется из ArrayObj, а не перегруженная. Без перегрузки этой функции и так всё работает, а весь вопрос и был в том, чтобы была возможность изменять Type()

А вот CreateElement интересная реализация. В тех примерах, что я прочёл до попыток что-то сделать, как-то по другому. Завтра попробую разобраться.

 
Alexey Viktorov #:

Спасибо. Но как и все ваши коды читать катастрофически сложно.

Написал предельно просто. Альтернативный пример.

И всё-же виртуальная функция в вашем коде используется из ArrayObj, а не перегруженная. Без перегрузки этой функции и так всё работает, а весь вопрос и был в том, чтобы была возможность изменять Type()

Честно говоря, не вижу необходимости использовать эту идентификационную функцию. Но если нужно, то пропишите ее так.

class CDeals : public CArrayObj
{
public:  
   //--- method of identifying the object
  virtual int Type() const override{ ::Print(__FUNCSIG__); return(sizeof(DEAL)) ; }

// ....

Распечатка покажет, что вызывается именно эта функция.

[MQL4/5] How to save/load/sort dynamic object collections?
[MQL4/5] How to save/load/sort dynamic object collections?
  • 2017.08.09
  • nicholish en
  • www.mql5.com
I wasn't able to find any documentation on how to override the CObject virtual methods Load, Save, and Compare to make use of(CList and CArrayObj...
 
fxsaber #:

Написал предельно просто. Альтернативный пример.

Честно говоря, не вижу необходимости использовать эту идентификационную функцию. Но если нужно, то пропишите ее так.

Распечатка покажет, что вызывается именно эта функция.

Большое спасибо за попытку помочь, но даже с переводчиком я прочесть не смогу. Так уж устроен мой старый мозг.

Острой необходимости в использовании этой идентификации нет. Просто интересно почему у меня в одном случае срабатывает, а в другом нет. В вашем коде работает, но понять мою ошибку у меня не получается.

На том, наверное, я и оставлю этот вопрос. Или может у кого-то получится пробить преграду в моём мозгу…

 
fxsaber #:

Ниже рабочий пример, где подсветил виртуальные функции.


Скрипт считывает историю в CArrayObj-объект, записывает ее в файл. Затем считывает ее из файла и распечатывает (для проверки корректности).

Всё-таки не могу успокоиться… Прошу объяснить:

Почему вы сделали два класса, один унаследованный от CObject

class CDeal : public CObject

и второй унаследованный от CArrayObj

class CDeals : public CArrayObj

Ведь в CArrayObj тоже есть виртуальные функции Save() и Load() и по идее они должны\могут быть переопределены в классе потомке…

Почему не в одном классе всё это?