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

 
Vladislav Boyko #:
Вот код, который отвечает на ваш вопрос.
class CParrent
  {
public:
   virtual string method(void) { return("parrent"); }
  };

class CChild : public CParrent
  {
public:
   string method(void) override { return("child"); }
  };

class CGrandson : public CChild
  {
public:
   string method(void) override { return("grandson"); }
  };

void OnStart()
  {
   CGrandson g;
   CParrent* ptr = GetPointer(g);
   Print(ptr.method()); // grandson
  }
Alexey Viktorov #:

То-есть в СБ отсутствие override к методу QuickSort() из CArrayObj() можно считать ошибкой?

Это не ошиба. Если вы не будете использовать override, то код будет работать так-же. Просто используя override вы сами себя страхуете от случайного изменения сигнатуры. Вы указываете компилятору, что метод с модификатором override должен переопределить метод базового класса, а если это не так (допустил опечатку в сигнатуре по невнимательности), то дай мне ошибку компиляции.

В добавок, при чтении кода вы сразу понимаете, какие методы из класса, который вы смотрите, переопределяют методы базового класса. Без override нужно открывать базовый класс и смотреть, какие методы там объявлены как виртуальные. В общем, перестраховка + улучшение читаемости.
 
Alexey Viktorov #:

То-есть в СБ отсутствие override к методу QuickSort() из CArrayObj() можно считать ошибкой?

Наверное не override, а virtual. Если так, то нет, это не ошибка. Главное, что метод указан как виртуальный в родительском классе...

 
Sergey Gridnev #:
Охрененно "понятное" объяснение.
Хотите сказать, что виртуальные функции не пишутся программистом? Или, что невиртуальные не вызываются системой?

Если бы я так думал, то и написал бы. А вы чисто по женски за меня что-то придумали и на основе своих фантазий обвиняете.

В MQL5, как и в других языках программирования, таких как C++, существует важное различие между перегруженными и виртуальными функциями. Вот основные отличия:

Перегруженные функции

  1. Определение: Перегрузка функций позволяет создавать несколько функций с одним и тем же именем, но с разными параметрами (типами, количеством или порядком). Это позволяет использовать одно и то же имя для разных операций, что делает код более читаемым.

  2. Пример:

    void MyFunction(int a) { /* ... */ }
    void MyFunction(double b) { /* ... */ }
    void MyFunction(int a, double b) { /* ... */ }
    
  3. Выбор функции: Компилятор выбирает, какую перегруженную функцию вызывать, на основе аргументов, переданных при вызове.

Виртуальные функции

  1. Определение: Виртуальные функции используются в контексте наследования и полиморфизма. Они позволяют производным классам переопределять поведение функции, определенной в базовом классе.

  2. Пример:

    class Base 
    {
    public:
        virtual void MyFunction() { /* базовая реализация */ }
    };
    
    class Derived : public Base 
    {
    public:
        void MyFunction() override { /* переопределенная реализация */ }
    };
  3. Выбор функции: При вызове виртуальной функции выбор реализации происходит во время выполнения (runtime) на основе типа объекта, а не типа указателя или ссылки. Это позволяет динамически изменять поведение программы. Для этого компилятор создает в памяти таблицу виртуальных функций и в рантайме ищет в ней нужную реализацию.Кстати, именно поэтому вирт. функции вызываются медленнее, чем обычные. 

Основные отличия

  • Контекст использования: Перегрузка используется для создания нескольких функций с одним именем, а виртуальные функции — для реализации полиморфизма и переопределения методов в наследуемых классах.
  • Выбор реализации: Перегруженные функции выбираются на этапе компиляции (compile-time), а виртуальные функции — на этапе выполнения (runtime).
  • Связь с наследованием: Виртуальные функции всегда связаны с классами и их наследованием, тогда как перегруженные функции могут существовать независимо от классов.
 
Vladislav Boyko #:

Это не ошиба. Если вы не будете использовать override, то код будет работать так-же. Просто используя override вы сами себя страхуете от случайного изменения сигнатуры. Вы указываете компилятору, что метод с модификатором override должен переопределить метод базового класса, а если это не так (допустил опечатку в сигнатуре по невнимательности), то дай мне ошибку компиляции.

В добавок, при чтении кода вы сразу понимаете, какие методы из класса, который вы смотрите, переопределяют методы базового класса. Без override нужно открывать базовый класс и смотреть, какие методы там объявлены как виртуальные. В общем, перестраховка + улучшение читаемости.
Denis Kirichenko #:

Наверное не override, а virtual. Если так, то нет, это не ошибка. Главное, что метод указан как виртуальный в родительском классе...

Наверное вы не поняли что я сказал.

Вот класс class CArray : public CObject и в нём виртуальная функция

   virtual void      QuickSort(int beg,int end,const int mode=0) { m_sort_mode=-1; }

И вот наследник class CArrayObj : public CArray в котором эта функция переопределена

   void              QuickSort(int beg,int end,const int mode);

её полный код

//+------------------------------------------------------------------+
//| Method QuickSort                                                 |
//+------------------------------------------------------------------+
void CArrayObj::QuickSort(int beg,int end,const int mode)
  {
   int      i,j;
   CObject *p_node;
   CObject *t_node;
//--- sort
   i=beg;
   j=end;
   while(i<end)
     {
      //--- ">>1" is quick division by 2
      p_node=m_data[(beg+end)>>1];
      while(i<j)
        {
         while(m_data[i].Compare(p_node,mode)<0)
           {
            //--- control the output of the array bounds
            if(i==m_data_total-1)
               break;
            i++;
           }
         while(m_data[j].Compare(p_node,mode)>0)
           {
            //--- control the output of the array bounds
            if(j==0)
               break;
            j--;
           }
         if(i<=j)
           {
            t_node=m_data[i];
            m_data[i++]=m_data[j];
            m_data[j]=t_node;
            //--- control the output of the array bounds
            if(j==0)
               break;
            j--;
           }
        }
      if(beg<j)
         QuickSort(beg,j,mode);
      beg=i;
      j=end;
     }
  }

Тут нет погремушки  override 

Вот я о чём…

 
Alexey Viktorov #:

Наверное вы не поняли что я сказал.

Вот класс class CArray : public CObject и в нём виртуальная функция

И вот наследник class CArrayObj : public CArray в котором эта функция переопределена

её полный код

Тут нет погремушки  override 

Вот я о чём…

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

Можете убрать из этого кода override и он будет работать точно так-же.

Но если вы допустите где-то опечатку (например, случайно указали немного другое имя функции), то при наличии override компилятор вам выдаст ошибку

class CParrent
  {
public:
   virtual string method(void) { return("parrent"); }
  };

class CChild : public CParrent
  {
public:
   string method(void) override { return("child"); }
  };

class CGrandson : public CChild
  {
public:
   string Method(void) override { return("grandson"); } // 'CGrandson::Method' method is declared with 'override' specifier, but does not override any base class method
  };

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

Alexey Viktorov #:

Так и я о том-же. Погремушка она и есть погремушка никому не нужная. Просто для удобства чтения.

Пару раз потратите время на дебаг на ровном месте и поймете, что с погремушкой сильно лучше, чем без нее
 
Vladislav Boyko #:

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

Можете убрать из этого кода override и он будет работать точно так-же.

Так и я о том-же. Погремушка она и есть погремушка никому не нужная. Просто для удобства чтения.

Ну да… После добавления вашего сообщения, согласен. Это когда пишешь что-то своё, для предупреждения ошибки… Полезно.
 
Alexey Viktorov #:

То-есть в СБ отсутствие override к методу QuickSort() из CArrayObj() можно считать ошибкой?

Если бы программист ошибся и написал QuicSort(), то компилятор пропустил бы эту ошибку, и в классе были бы 2 метода: родительский QuickSort() и собственный QuicSort(). Эта ошибка могла бы остаться незамеченной до поры до времени.

Но если бы программист написал QuicSort() override, то компилятор не пропустил бы эту ошибку и она была бы исправлена незамедлительно.
 
Sergey Gridnev #:
Если бы программист ошибся и написал QuicSort(), то компилятор пропустил бы эту ошибку, и в классе были бы 2 метода: родительский QuickSort() и собственный QuicSort(). Эта ошибка могла бы остаться незамеченной до поры до времени.

Но если бы программист написал QuicSort() override, то компилятор не пропустил бы эту ошибку и она была бы исправлена незамедлительно.

Однажды я случайно использовал кирилличную С в названии папки и долго не мог понять, почему компилятор не находит .mqh файл

Из-за использования подсказок, иногда бывает, что я замечаю опечатку в идентификаторе после того, как сослался на него уже десятки раз. Обычно следующим утром приходит озарение. Какой-нибудь условный "Direciton"
 
Vladislav Boyko #:
Однажды я случайно использовал кирилличную С в названии папки и долго не мог понять, почему компилятор не находит .mqh файл

Так видно же:


 
Artyom Trishkin #:

Так видно же:

В проводнике. Имя папки содержало кирилличную букву, а в ME я указывал английскую.

К стати, если это строковый литерал в ME, то не будет видно.