Полиморфизм C++. Основы объектно-ориентированного программирования

      Комментарии к записи Полиморфизм C++. Основы объектно-ориентированного программирования отключены

Главная Форумы Программирование Программирование на С++ Учебные материалы по С++ Полиморфизм C++. Основы объектно-ориентированного программирования

В этой теме 1 ответ, 2 участника, последнее обновление  Васильев Владимир Сергеевич 10 мес., 2 нед. назад.

  • Автор
    Сообщения
  • #2863

    questioner
    Участник

    Объясните что в объектно-ориентированном программировании называют полиморфизмом. В литературе все очень запутанно, например в википедии есть следующая фраза:

    В сообществе объектно-ориентированного программирования под термином «полиморфизм» обычно подразумевают наследование.

    Я знаю что такое наследование, но если полиморфизм — это наследование, то зачем лишний термин?

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

    Полиморфизм — центральное понятие в ООП, обозначающее способность объекта выбирать правильную функцию в зависимости от типа данных, полученных в сообщении (message). Благодаря полиморфизму объект выполняет нужные действия, даже если содержимое сообщения было неизвестно во время написания программы.

    Поясните что все это значит и приведите примеры таких случаев (когда тип объекта не известен сразу).

  • #2865

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

    cplusplus_polymorphism_example

    В этом проекте используется графическая сцена (QGraphicsScene), отображающая все игровые элементы (ежика, тучки и т.п.). На самом деле, все эти графические элементы являются наследниками класса QGraphicsPixmapItem, представляющего собой картинку на графической сцене. Стандартная графическая сцена ничего не знает о наших ежиках, но корректно их отображает за счет использования виртуальных функций. Например, в классе AnimatedGraphicsItem, отвечающего за отображение анимированного графического элемента, реализуется виртуальная функция boundingRect класса QGraphicsItem — за счет этого графическая сцена узнает размер элементов. Внутри графической сцены в каком-либо виде хранится набор указателей на QGraphicsItem (они добавляются функцией QGraphicsScene::addItem(QGraphicsItem * item)), при этом сцена может вызвать, например, код из класса WallCloud за счет использования механизма виртуальных функций и позднего связывания:

    QGraphicsScene scene;
    QGraphicsItem *item;
    if (condition) {
      item = new WallCloud();
    }
    else {
      item = new StrongCloud();
    }
    
    scene.addItem(item);

    Статический тип объекта (известный во время компиляции) — QGraphicsItem, а динамический (реальный тип, известный лишь во время выполнения) — WallCloud или StrongCloud. При вызове невиртуальной функции по указателю item будет выбрана функция, соответствующая статическому типу объекта (раннее связывание, на этапе компиляции) , но при вызове виртуальной функции — динамическому (позднее связывание, на этапе выполнения).

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

    В этом же примере класс GameWidget содержит внутри себя двумерный массив тучек (vector<vector<CloudElement* >>). На самом деле внутри массива будут экземпляры классов WallCloud, Cloud и StrongCloud, т.е. опять используется полиморфизм. Что это дает?

    1. внутри одного массива мы можем хранить реальные (динамические) типы которых отличаются за счет того, что их статический тип одинаков (все они имеют один базовый класс);
    2. базовый класс задает интерфейс, через который мы можем работать с объектами. Заметьте, что мы могли бы хранить массив указателей на GameElement (т.к. он является базовым для всех CloudElement) или даже QGraphicsItem. Однако, наши тучки должны уметь, например, взрываться (виртуальная функция bang()), мы не сможем вызывать эту функцию, имея указатель на QGraphicsItem, т.к. он не содержит такой функции. Поэтому мы объявляем специальный класс, задающий интерфейс и порождаем от него конкретные виды объектов, реализующих интерфейс, а затем работаем с объектами через указатель на класс интерфейса.

Для ответа в этой теме необходимо авторизоваться.