Полиморфизм это в программировании

Полиморфизм это в программировании

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

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

Для квадрата (класс CSquare) площадь вычисляется через стороны, для круга (класс CCircle) площадь выражается через радиус и так далее. Мы можем создать массив для хранения объектов типа CShape, в котором можно будет хранить как объект базового класса, так и всех его потомков. В дальнейшем мы можем вызывать одну и ту же функцию для любого элемента данного массива.

//— Базовый класс
class CShape
<
protected :
int m_type; // тип фигуры
int m_xpos; // X — координата точки привязки
int m_ypos; // Y — координата точки привязки
public :
void CShape(); // конструктор, тип равен нулю
int GetType()< return (m_type);>; // возвращает тип фигуры
virtual
double GetArea() < return ( 0 ); >// возвращает площадь фигуры
>;

Теперь все производные классы имеют функцию-член getArea(), которая возвращает нулевое значение. Реализация этой функции в каждом потомке будет отличаться.

//— производный класс Круг
class CCircle : public CShape // после двоеточия указывается базовый класс,
< // от которого производится наследование
private :
double m_radius; // радиус круга

public :
void CCircle(); // конструктор, тип равен 1
void SetRadius( double r);
virtual double GetArea() < return ( 3 . 14 *m_radius*m_radius);>// площадь круга
>;

Для квадрата объявление класса выглядит аналогично:

//— производный класс Квадрат
class CSquare : public CShape // после двоеточия указывается базовый класс,
< // от которого производится наследование
private :
double m_square_side; // сторона квадрата

public :
void CSquare(); // конструктор, тип равен 2
void SetSide( double s);
virtual double GetArea() < return (m_square_side*m_square_side);>//площадь квадрата
>;

Так как для вычисления площади квадрата и круга требуются соответствующие значения членов m_radius и m_square_side, то в объявлении соответствующего класса мы добавили функции SetRadius() и SetSide().

Предполагается, что в программе у нас используются объекты разного типа (CCircle и CSquare), но унаследованные от одного базового типа CShape. Полиморфизм позволяет нам создать массив объектов базового типа CShape, но при объявлении этого массива сами объекты еще неизвестны и их тип неопределен.

Решение о том, объект какого типа будет содержаться в каждом элементе массива, будет приниматься непосредственно при выполнении программы. Это подразумевает динамическое создание объектов соответствующих классов, и, следовательно, необходимость вместо самих объектов использовать указатели объектов.

Для динамического создания объектов используется оператор new, каждый такой объект нужно самостоятельно и явно удалять оператором delete. Поэтому мы объявим массив указателей типа CShape и для каждого его элемента создадим объект нужного типа ( new Имя_класса) , как это показано в примере скрипта:

//+——————————————————————+
//| Script program start function |
//+——————————————————————+
void OnStart ()
<
//— объявим массив указателей объектов базового типа
CShape *shapes[5]; // массив указателей на объекты CShape

//— здесь заполняем массив производными объектами
//— объявим указатель на объект типа CCircle
CCircle *circle= new CCircle();
//— задаем свойства объекта по указателю circle
circle.SetRadius(2.5);
//— поместим в shapes[0] значение указателя
shapes[0]=circle;

//— создаем еще один объект CCircle и запишем его указатель в shapes[1]
circle= new CCircle();
shapes[1]=circle;
circle.SetRadius(5);

//— тут мы намеренно "забыли" задать значение для shapes[2]
//circle=new CCircle();
//circle.SetRadius(10);
//shapes[2]=circle;

//— для неиспользуемого элемента установим значение NULL
shapes[2]= NULL ;

//— создаем объект CSquare и запишем его указатель в shapes[3]
CSquare *square= new CSquare();
square.SetSide(5);
shapes[3]=square;

//— создаем объект CSquare и запишем его указатель в shapes[4]
square= new CSquare();
square.SetSide(10);
shapes[4]=square;

//— массив указателей есть, получим его размер
int total= ArraySize (shapes);
//— пройдем в цикле по всем указателям в массиве
for ( int i=0; i
<
//— если по указанному индексу указатель является валидным
if ( CheckPointer (shapes[i])!= POINTER_INVALID )
<
//— выведем в лог тип и площадь фигуры
PrintFormat ( "Объект типа %d имеет площадь %G" ,
shapes[i].GetType(),
shapes[i].GetArea());
>
//— если указатель имеет тип POINTER_INVALID
else
<
//— сообщим об ошибке
PrintFormat ( "Объект shapes[%d] не инициализирован! Его указатель %s" ,
i, EnumToString ( CheckPointer (shapes[i])));
>
>

Читайте также:  Картинки на вк для пацанов крутые новые

//— мы должны самостоятельно уничтожить все созданные динамические объекты
for ( int i=0;i
<
//— удалять можно только объекты, чей указатель имеет тип POINTER_DYNAMIC
if ( CheckPointer (shapes[i])== POINTER_DYNAMIC )
<
//— сообщим об удалении
PrintFormat ( "Удаляем shapes[%d]" ,i);
//— уничтожим объект по его указателю
delete shapes[i];
>
>
>

Обратите внимание, что при уничтожении объекта оператором delete необходимо проверить тип его указателя. Удалять с помощью delete можно только объекты, имеющие указатель POINTER_DYNAMIC, для указателей другого типа будет получена ошибка.

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

Объектно-ориентированное программирование ( ООП ) – подход к созданию программ, основанный на использовании классов и объектов, взаимодействующих между собой.

Класс (java class) описывает устройство и поведение объектов. Устройство описывается через набор характеристик (свойств), а поведение – через набор доступных для объектов операций (методов). Классы можно создавать на основе уже имеющихся, добавляя или переопределяя свойства и методы.

Классы представляют шаблоны, по которым строятся объекты. Объекты – это элементы программы, обладающие схожим набором характеристик и поведением (т.е это элементы, построенные на основе одного класса). Каждый объект имеет некоторое состояние, оно определяется значением всех его свойств. В одной программе могут существовать несколько классов, а объекты разных классов могут взаимодействовать между собой (через методы).

Наследование, extends

Наследование является неотъемлемой частью Java. При использовании наследования принимается во внимание, что новый класс, наследующий свойства базового (родительского) класса имеет все те свойства, которым обладает родитель. В коде используется операнд extends, после которого указывается имя базового класса. Тем самым открывается доступ ко всем полям и методам базового класса.

Используя наследование, можно создать общий "java class", который определяет характеристики, общие для набора связанных элементов. Затем можно наследоваться от него и создать дополнительные классы, для которых определить дополнительные уникальные для них характеристики.

Главный наследуемый класс в Java называют суперклассом super. Наследующий класс называют подклассом. Таким образом подкласс — это специализированная версия суперкласса, которая наследует все свойства суперкласса и добавляет свои собственные уникальные элементы.

Рассмотрим пример описания java class’a студента Student, который имеет имя, фамилию, возраст, и номер группы. Класс студента будем создавать на основе super класса пользователя User, у которого уже определены имя, фамилия и возраст:

Теперь создаем отдельный класс Student, наследующего свойства super класса. При наследовании класса необходимо также переопределить и конструкторы родительского класса :

Ключевое слово extends показывает, что мы наследуемся от класса User.

Ключевое слово super

В конструкторе класса Student мы вызываем конструктор родительского класса через оператор super, передавая ему весь необходимой набор параметров. В Java ключевое слово super обозначает суперкласс, т.е. класс, производным от которого является текущий класс. Ключевое слово super можно использовать для вызова конструктора суперкласса и для обращения к члену суперкласса, скрытому членом подкласса.

Рассмотрим как происходит наследование с точки зрения создания объекта :

Сначала открывается конструктор класса Student, после этого вызывается конструктор суперкласса User, а затем выполняются оставшиеся операции в конструкторе Student. Такая последовательность действий вполне логична и позволяет создавать более сложные объекты на основе более простых.

У суперкласса могут быть несколько перегруженных версий конструкторов, поэтому можно вызывать метод super() с разными параметрами. Программа выполнит тот конструктор, который соответствует указанным аргументам.

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

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

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

Переопределение методов, Override

Если в иерархии классов имя и сигнатура типа метода подкласса совпадает с атрибутами метода суперкласса, то метод подкласса переопределяет метод суперкласса. Когда переопределённый метод вызывается из своего подкласса, он всегда будет ссылаться на версию этого метода, определённую подклассом. А версия метода из суперкласса будет скрыта.

Читайте также:  Как понять какая у тебя видеокарта

Если нужно получить доступ к версии переопределённого метода, определённого в суперклассе, то необходимо использовать ключевое слово super.

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

В Java SE5 появилась анотация @Override;. Если необходимо переопределить метод, то используйте @Override, и компилятор выдаст сообщение об ошибке, если вместо переопределения будет случайно выполнена перегрузка.

В Java можно наследоваться только от одного класса.

Инкапсуляция

В информатике инкапсуляцией (лат. en capsula) называется упаковка данных и/или функций в единый объект.

Основой инкапсуляции в Java является класс. Инкапсуляция означает, что поля объекта недоступны его клиентам непосредственно — они скрываются от прямого доступа извне. Инкапсуляция предохраняет данные объекта от нежелательного доступа, позволяя объекту самому управлять доступом к своим данным.

Модификаторы доступа

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

Модификатор доступа Область действия
public Без ограничений
private Только из данного класса
protected Из данного класса и его потомков
Без модификатора Для всех классов данного пакета

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

Желательно использовать доступ к свойствам класса только через его методы (принцип bean классов, "POJO"), который позволяет валидировать значения полей, так как прямое обращение к свойствам отслеживать крайне сложно, а значит им могут присваиваться некорректные значения на этапе выполнения программы. Такой принцип относится к управлению инкапсулированными данными и позволяет быстро изменить способ хранения данных. Если данные станут храниться не в памяти, а в файлах или базе данных, то потребуется изменить лишь ряд методов одного класса, а не вводить эту функциональность во все части системы.

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

Пример простого описания робота

В представленном примере робота используются наборы методов, начинающие с set и get. Эту пару методов часто называют сеттер/геттер. Данные методы используются для доступа к полям объекта. Наименования метода заканчиваются наименованием поля, начинающееся с ПРОПИСНОЙ буквы.

В методах set мы передаем значение через формальный параметр во внутрь процедуры. В коде процедуры мы присваиваем значение переменной объекта/класса с использованием ключевого слова this.

Использование ключевого слова this необходимо, т.к. наименование формального параметра совпадает с наименованием переменной объекта. Если бы наименования отличались бы, то можно было бы this не использавать.

Полиморфизм, polymorphism

Полиморфизм является одним из фундаментальных понятий в объектно-ориентированном программировании наряду с наследованием и инкапсуляцией. Слово полиморфизм греческого происхождения и означает "имеющий много форм". Чтобы понять, что означает полиморфизм применительно к объектно-ориентированному программированию, рассмотрим пример создания векторного графического редактора, в котором необходимо использовать ряд классов в виде набора графических примитивов — Square, Line, Circle, Triangle, и т.д. У каждого из этих классов необходимо определить метод draw для отображения соответствующего примитива на экране.

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

Человек, незнакомый с полиморфизмом, вероятнее всего создаст несколько массивов: отдельный массив для каждого типа примитивов и напишет код, который последовательно переберет элементы из каждого массива и вызовет у каждого элемента метод draw. В результате получится примерно следующий код:

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

Читайте также:  Сохраните файл конфигурации в nvram

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

После этого мы создаем различные классы-наследники: Square (Квадрат), Line (Линия), Сircle (круг) и Triangle (Треугольник):

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

Теперь проверим удивительную возможность полиморфизма:

В консоль будут выведены следующие строки:

Таким образом каждый класс-наследник вызвал именно свой метод draw, вместо того, чтобы вызвать метод draw из родительского класса Shape.

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

Перегрузка метода, overload

В процедурном программировании тоже существует понятие полиморфизма, которое отличается от рассмотренного механизма в ООП. Процедурный полиморфизм предполагает возможность создания нескольких процедур или функций с одинаковым именем, но разными количеством или типами передаваемых параметров. Такие одноименные функции называются перегруженными, а само явление — перегрузкой (overload). Перегрузка функций существует и в ООП и называется перегрузкой методов. Примером использования перегрузки методов в языке Java может служить класс PrintWriter, который используется в частности для вывода сообщений на консоль. Этот класс имеет множество методов println, которые различаются типами и/или количеством входных параметров. Вот лишь несколько из них:

Язык С++. Полиморфизм

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

Полиморфизм по-гречески означает «много форм». Объекты, имеющие общего предка, могут принимать разные формы, оставаясь при этом схожими.

Чтобы использовать полиморфизм, необходимо чтобы:

1) все классы-потомки являлись наследниками одного и того же базового класса

2) функция, реализующая метод, должна быть объявлена виртуальной в базовом классе

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

Доступ к обычным методам через указатели

Рассмотрим пример, когда базовый и производные классы содержат функции с одни и тем же именем, и к ним обращаются с помощью указателей, но без использования виртуальных функций:

A, B, Base – это типы. Указатели на объекты производных классов совместимы по типу с указателями на объекты базового класса.

Base *ptr; ptr=&a; ptr=&b;

Однако, указатели производных классов между собой не совместимы!

Пример:

A *ptr; ptr=&a;

ptr=&b; // указатель класса A не совместим с указателем класса B.

Теперь необходимо понять, какая собственно функция выполняется в этой строчке

Это функция Base::show() или A::show() или B:show()?

Результат выполнения дает простой ответ

Base

Base

Всегда выполняется метод базового класса. Компилятор не смотрит на содержимое указателя, а выбирает метод, определяемый типом указателя!!

Доступ к виртуальным методам через указатели

Сделаем одно маленькое изменение в нашей программе: поставим ключевое слово virtual перед объявлением функции show() в базовом классе.

На выходе имеем:

Class A

Class B

Теперь выполняются методы производных классов. Один и тот же вызов ставит на выполнение разные функции в зависимости от содержимого указателя ptr.

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

Абстрактные классы и чисто виртуальные методы

Базовый класс, объекты которого никогда не будут реализованы называется абстрактным классом. Такой класс может существовать с единственной целью – быть родительским классом к производным классом, объекты которых будут реализованы.

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

Чисто виртуальная функция – это функция, после объявления которой добавлено выражение =0.

Пример: Объявить абстрактный класс person. Объявить два производных класса – student и teacher. В каждом из классов объявить метод, с помощью которого можно создать список выдающихся педагогов и студентов. Студентов со средним баллом больше 4 и педагогов с числом публикаций более 50 статей считать выдающимися.

Ссылка на основную публикацию
Нет msvcr120 dll что делать
Если, попытавшись включить любимую игру, вы натыкаетесь на окно, которое гласит, что запуск программы невозможен по причине отсутствия mscvr120.dll —...
Консольные команды для бателфилд 4
Встречаем и вновь возвращаемся в самый: динамический, красивый, технически богатый и самый заселённый мир с постоянно ведущимися боевыми действиями. Самый...
Конструкция степлера канцелярского схема
Первые степлеры появились во Франции в XVIII веке, их специально изобрели для короля Людовика XV. Но в то время это...
Нет беспроводного сетевого соединения windows 7
На панели задач в Windows или в меню «Центр управление сетями» нет иконки Wi-Fi? Это не значит, что вышло из...
Adblock detector