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

 
fxsaber #:

Виртуальные методы полезны только при использовании указателей. Если указатели не используются, virtual не пригодится.


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

И если используются указатели, то можно собаку обобщить, сказав всем, что это Животное (указатель). Но при этом оно будет лаять.


В итоге можно, например, наплодить (создать объекты) собак, кошек и баранов. Затем загнать их в один одинаковый загон Животных (массив указателей на Животных). И, вроде, в массиве все Животные, но звук будет каждый издавать свой: лаять, мяукать, блеять.

Хорошее объяснение!

 
Alexey Viktorov #:

В чём прикол? В чём полезность виртуальности?

В том, что с помощью виртуальных методов реализуется полиморфизм - один из столпов ООП. Вот представьте себе стадо (группу) зверей, о котором писали выше. Там есть кошки, собаки, бараны и т.д. Пусть берём в цикле каждое животное и просим, чтобы оно издало звук. Заранее мы не знаем, кто нам попадётся. Но у каждого есть свой виртуальный метод озвучивания. И на этапе выполнения программы (run-time) он должен сработать и правильно озвучить выбранное животное. 

 
fxsaber #:

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

И если используются указатели, то можно собаку обобщить, сказав всем, что это Животное (указатель). Но при этом оно будет лаять.


В итоге можно, например, наплодить (создать объекты) собак, кошек и баранов. Затем загнать их в один одинаковый загон Животных (массив указателей на Животных). И, вроде, в массиве все Животные, но звук будет каждый издавать свой: лаять, мяукать, блеять.

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

Гораздо сложнее придумать простой пример для виртуальных методов, когда использование реализации базового класса может быть уместным на ряду с ее заменой производными классами. Лично я не могу простой пример придумать для этого.

 
Alexey Viktorov #:

То-есть функция в потомке просто заменяет функцию родителя?

Если так, то зачем всё это городить если простая функция будет работать так-же…

Да, можно перезагрузить функцию в потомке и она заменит родительскую.

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

Учёба. Классы. Нужна помощь.

fxsaber, 2024.11.22 19:54

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

Например, если захотеть посмотреть процитированный исходник, то там будет использоваться слово virtual. Но для его работоспособности этот virtual не нужен, т.к.

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

Учёба. Классы. Нужна помощь.

fxsaber, 2024.12.16 10:30

Виртуальные методы полезны только при использовании указателей. Если указатели не используются, virtual не пригодится.

Т.е. все будет работать через механизм перегрузки, если убрать virtual.


Однако, по какой-то причине virtual там написан. Причина в том, что потенциально в будущем возможна работа с этими объектами через указатели. И тогда механизм перегрузки будет давать совсем не тот эффект, что потребуется. Прописывание виртуальности определяет разработчик часто на основе просто опыта/интуиции. Опытные почти сразу видят, что может быть virtual и прописывают. Даже если пока не собираются это использовать. Примерно так и получилось с CTrade.

 
Vladislav Boyko #:

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

Гораздо сложнее придумать простой пример для виртуальных методов, когда использование реализации базового класса может быть уместным на ряду с ее заменой производными классами. Лично я не могу простой пример придумать для этого.

Может так случиться, что многие животные не издают звуков. Тогда базовая виртуальная функция звука животного - тишина. И нам нужно писать реализацию звука нового животного только тогда, когда мы знаем, как оно звучит. Не знаем - не прописываем. Т.е. удобно (код лаконичный), когда базовая виртуальная функция прописана.

 
Vladislav Boyko #:

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

Можно сделать и реализацию в базовом классе как метод по умолчанию. Например:

class CiAnimal
   {
   public:
      virtual void Sound()
         {
         Print("silent");
         }; // звук, который издает животное
   };
//+------------------------------------------------------------------+
//|  Класс CCat наследуется от класса CiAnimal                   |
//+------------------------------------------------------------------+
class CCat : public CiAnimal
   {
   public:
      CCat()
         {
         Print("Cat was born");
         }
      ~CCat()
         {
         Print("Cat is dead");
         }
      void Sound() override
         {
         Print("meou");
         }
   };
//+------------------------------------------------------------------+
//|  Класс CDog наследуется от класса CiAnimal                       |
//+------------------------------------------------------------------+
class CDog : public CiAnimal
   {
   public:
      CDog()
         {
         Print("Dog was born");
         }
      ~CDog()
         {
         Print("Dog is dead");
         }
      void Sound() override
         {
         Print("guaf");
         }
   };
//+------------------------------------------------------------------+
//|  Класс CTurtle наследуется от класса CiAnimal                    |
//+------------------------------------------------------------------+
class CTurtle : public CiAnimal
   {
   public:
      CTurtle()
         {
         Print("Turtle was born");
         }
      ~CTurtle()
         {
         Print("Turtle is dead");
         }
   };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
   {
   //--- массив указателей на объекты типа IAnimal
   CiAnimal *animals[3];
   //--- породим потомков CiAnimal и сохраним указатели на них в массив
   animals[0] = new CCat;
   animals[1] = new CDog;
   animals[2] = new CTurtle;
   //--- вызовем метод Sound() для каждого потомка
   for(int i = 0; i < ArraySize(animals); ++i)
      animals[i].Sound();
   //--- удалим объекты
   for(int i = 0; i < ArraySize(animals); ++i)
      delete animals[i];
   }
//+------------------------------------------------------------------+


Т.е. если у животного нет своей реализации звука, то предполагается, что оно молчит ))

 
Denis Kirichenko #:

В том, что с помощью виртуальных методов реализуется полиморфизм - один из столпов ООП. Вот представьте себе стадо (группу) зверей, о котором писали выше. Там есть кошки, собаки, бараны и т.д. Пусть берём в цикле каждое животное и просим, чтобы оно издало звук. Заранее мы не знаем, кто нам попадётся. Но у каждого есть свой виртуальный метод озвучивания. И на этапе выполнения программы (run-time) он должен сработать и правильно озвучить выбранное животное. 

Вот такими объяснениями, простите загажены, все учебники и примеры. Это ни о чём. Мне кажется это легко решается обычной перегрузкой функции… Это не утверждение, могу и заблуждаться…

 
Alexey Viktorov #:
Если так, то зачем всё это городить если простая функция будет работать так-же… В чём прикол? В чём полезность виртуальности?

Перечитайте оба моих сообщения вдумчиво. Простая перегрузка методов не будет работать также. Класс CArray оперирует объектами CObject, он не знает про наследников. Если Compare не виртуальный, то будет вызван метод Compare класса CObject, а если виртуальный - класса-потомка.



Учёба. Классы. Нужна помощь. - Что такое виртуальная функция?
Учёба. Классы. Нужна помощь. - Что такое виртуальная функция?
  • 2024.12.16
  • lynxntech
  • www.mql5.com
что могут быть созданы потомки, которые реализуют коллекции объектов CObject. Это метод как раз и будет вызывать метод Compare у элементов коллекции. Этот объект передается в метод Compare посредством ссылки node. Вы реализуете свой класс CMyObject как потомок CObject
 
fxsaber #:
Denis Kirichenko #:

Да, логично. Но я бы наверное предпочел обязать себя явно реализовать молчание. Просто чтобы в дальнейшем ничего не провтыкать.

class CAnimal
  {
public:
   virtual void speak() = 0;
  };

class CFish : public CAnimal
  {
private:
   void speak() override {}
  };

Я бы еще отладочную информацию добавил:

class CAnimal
  {
public:
   virtual void speak() = 0;
  };

class CFish : public CAnimal
  {
private:
   void speak() override;
  };

void CFish::speak(void)
  {
   #ifdef CUSTOM_DEBUG_MODE
      Print("[DEBUG] "__FUNCTION__" Кто-то пытался поговорить с рыбой через указатель на базовый класс");
   #endif 
  }

[edit]

Хотя, возможно не стояло прятать speak() в приватную секцию для рыбы.

[edit 2]

Та не, она же виртуальная, можно и спрятать, я думаю. Ведь область видимости виртуальных методов всегда указывается заново в производном классе.

 
Alexey Viktorov #:

Вот такими объяснениями, простите загажены, все учебники и примеры. Это ни о чём. Мне кажется это легко решается обычной перегрузкой функции… Это не утверждение, могу и заблуждаться…

Алексей, у Вас есть критическое мышление, что продвигает Вас на шаг ближе ))

Вот возьмём мой пример выше и просто переопределим метод озвучивания.

class CiAnimal
   {
   public:
      void Sound()
         {
         Print("silent");
         }; // звук, который издает животное
   };
//+------------------------------------------------------------------+
//|  Класс CCat наследуется от класса CiAnimal                       |
//+------------------------------------------------------------------+
class CCat : public CiAnimal
   {
   public:
      CCat()
         {
         Print("Cat was born");
         }
      ~CCat()
         {
         Print("Cat is dead");
         }
      void Sound()
         {
         Print("meou");
         }
   };
//+------------------------------------------------------------------+
//|  Класс CDog наследуется от класса CiAnimal                       |
//+------------------------------------------------------------------+
class CDog : public CiAnimal
   {
   public:
      CDog()
         {
         Print("Dog was born");
         }
      ~CDog()
         {
         Print("Dog is dead");
         }
      void Sound()
         {
         Print("guaf");
         }
   };
//+------------------------------------------------------------------+
//|  Класс CTurtle наследуется от класса CiAnimal                    |
//+------------------------------------------------------------------+
class CTurtle : public CiAnimal
   {
   public:
      CTurtle()
         {
         Print("Turtle was born");
         }
      ~CTurtle()
         {
         Print("Turtle is dead");
         }
   };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
   {
   //--- массив указателей на объекты типа CiAnimal
   CiAnimal *animals[3];
   //--- породим потомков CiAnimal и сохраним указатели на них в массив
   animals[0] = new CCat;
   animals[1] = new CDog;
   animals[2] = new CTurtle;
   //--- вызовем метод Sound() CiAnimal для каждого потомка
   for(int i = 0; i < ArraySize(animals); ++i)
      animals[i].Sound();
   //--- удалим объекты
   for(int i = 0; i < ArraySize(animals); ++i)
      delete animals[i];
   }
//+------------------------------------------------------------------+

В журнале мы увидим такое:

2024.12.16 13:39:00.302 t3 (SBER,M5)    Cat was born
2024.12.16 13:39:00.302 t3 (SBER,M5)    Dog was born
2024.12.16 13:39:00.302 t3 (SBER,M5)    Turtle was born
2024.12.16 13:39:00.302 t3 (SBER,M5)    silent
2024.12.16 13:39:00.302 t3 (SBER,M5)    silent
2024.12.16 13:39:00.302 t3 (SBER,M5)    silent
2024.12.16 13:39:00.302 t3 (SBER,M5)    Cat is dead
2024.12.16 13:39:00.302 t3 (SBER,M5)    Dog is dead
2024.12.16 13:39:00.302 t3 (SBER,M5)    Turtle is dead


Т.е. программа всегда вызывала метод родительского класса, без учёта классовой иерархии.