Учёба. Классы. Нужна помощь. - страница 20

 
Artyom Trishkin #:

Ты это серьёзно? Я же тебе всё объяснял, и ты сам всё делал потом. Ещё и примерами своими меня завалил...

Я это делал тупо исправляя то так, то иначе то, что написал ты. И только под конец причесал с использованием шаблонной функции.

А вот написать что-то самостоятельно не смогу. Потому, что не понимаю как это работает.

Грузить тебя в личку не могу. Мне слишком жалко твоё время. Потому и решил, что тут коллегиально получится мне донести хоть немного…

 
lynxntech #:
вот уж мастер примеров)) перегрузка сама подбирает по подходящим входным данным, где там что в ручную делается?
Я только сейчас понял, он имел ввиду переопределение методов, т.е. method overriding.
 
lynxntech #:

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


Прежде чем нести очередной бред, возьми ради интереса в MetaEditor забей в поиске файлов вот такое:



Удивишься результату.



 

По поводу применимости виртуальных:

Возможно, я делаю не совсем по уму, но так сложилось в процессе самообучения )

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

И в этом классе есть чисто виртуальные ф-ции проверки условий на открытие и закрытие позиций. А в производных классах эти функции переопределяются.

Соответственно, есть массив указателей на базовый класс, но лежат там указатели на производные.

На тике (упрощённо) в цикле перебираются все экземпляры роботов в советнике и вызываются эти их функции, реально вызывается своя ф-ция для экземпляров каждого класса.

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

 
JRandomTrader #:


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

Статические функции принципиально не могут быть виртуальными - это взаимоисключающие понятия. 

Виртуальная функция имеет указатель this на объект, к которому она относится. Статическая функция - относится либо к классу, либо к глобальному пространству, и указателя this не имеет.  Соответственно, статическая функция не может иметь доступа к нестатическим членам класса (для них нужен указатель this). 

Лично я использую статические функции для "обслуживающих" процедур, которые относятся к классу, однако, не требуют доступа к нестатическим членам. Ну, скажем, функции преобразования перечислений. Или, скажем, функции, работающие только со статическими членами класса - очень удобно, когда есть одна статическая переменная, доступная для всех объектов данного класса, соответственно, для работы с ней нередко удобны как раз статические функции. 


А как по-твоему, должна работать "статическая виртуальная функция" ? У неё есть указатель this? 

 

Ещё пример использования виртуальных методов.

Упоминался как-то выше метод CObject::Compare(). Вот его полное определение:

virtual int       Compare(const CObject *node,const int mode=0) const { return(0);      }


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


//+------------------------------------------------------------------+
//| CSignalData class                                                |
//+------------------------------------------------------------------+
class CSignalData : public CObject
   {
      //--- === Data members === ---
   private:
      datetime       m_dtime;  // datetime
      string         m_symbol; // symbol
      double         m_profit; // profit
      //--- === Methods === ---
   public:
      // constructor/destructor
      CSignalData(datetime _dtime, string _symbol, double _profit);
      ~CSignalData(void) {};
      //---
      datetime       GetDtime(void) const;
      string         GetSymbol(void) const;
      double         GetProfit(void) const;
      //--- method of comparing the objects
      virtual int    Compare(const CObject *_ptr_other_sig, const int mode = 0) const;
   };


Допустим, что метод CSignalData::Compare() должен сравнивать 2 объекта сигналов по времени (m_dtime) - текущий и другой (задаётся как параметр метода - _ptr_other_sig). И тут есть нюанс. Сравнивать мы можем только объекты типа CSignalData. Но с другой стороны, нам нужно сохранить сигнатуру метода: тип параметра _ptr_other_sig должен оставаться как CObject*.

Как быть? ))

 
Denis Kirichenko #:
Как быть? ))

dynamic_cast, наверное. Но это сильно сомнительное удовольствие, ка по мне. Я бы не наследовался от CObject, а написал бы свои велосипедные сравнения и сортировки.

И то, хз, какие там подводные камни. Ни разу так не делал.
 
Vladislav Boyko #:

dynamic_cast, наверное. Но это сильно сомнительное удовольствие, ка по мне. Я бы не наследовался от CObject, а написал бы свои велосипедные сравнения и сортировки.

Да, согласен. Тут проверку компилятор не делает. А есть какое-то другое решение при всех исходных?

Vladislav Boyko #:

И то, хз, какие там подводные камни. Ни разу так не делал.


"Ни разу не делал" не означает, что нельзя. Просто нужно быть уверенным на 100%, что подаёшь на вход валидные данные. Вот моё решение. И пока не подводило.

//+------------------------------------------------------------------+
//| Method of comparing the objects                                  |
//+------------------------------------------------------------------+
int CSignalData::Compare(const CObject *_ptr_other_sig, const int mode = 0) const
   {
   int res = 0;
//---
   if(mode == 0)
      {
      const CSignalData* ptr_other_signal = dynamic_cast<const CSignalData*>(_ptr_other_sig);
      datetime curr_sig_dtime, other_sig_dtime;
      curr_sig_dtime = GetDtime();
      other_sig_dtime = ptr_other_signal.GetDtime();
      if(curr_sig_dtime > other_sig_dtime)
         res = 1;
      else if(curr_sig_dtime < other_sig_dtime)
         res = -1;
      }
//---
   return res;
   }
 
Denis Kirichenko #:

Да, согласен. Тут проверку компилятор не делает. А есть какое-то другое решение при всех исходных?

Я не знаю другого решения. Более того, я ни разу не делал ничего подобного даже с dynamic_cast.

Этот пример, скорее всего, просто упадет при попытке сравнить мягкое с теплым (довольно долго я вкуривал, где там кастуются указатели). Там есть проверки указателя и логирование. Но мне все еще тяжело представить полную картину из того примера. Наверное упадет, но логи оставит и в дебаг режиме сделает DebugBreak

 
Denis Kirichenko #:
Вот моё решение. И пока не подводило.

А где проверка результата преобразования😄? Хотя, в рантайме терминал и так проверит указатель перед обращением к нему. Если из кода проверять указатель, то нужно что-то делать если передали не то, что ожидали. А из Compare "что-то делать" не сильно удобно и не сильно хочется. Даже эксепшин бросать мне не очень хотелось бы.