Перейти к содержанию
СофтФорум - всё о компьютерах и не только

вопрос начинающего программиста по ООП


Гость avb_constructor

Рекомендуемые сообщения

Народ, есть вопросик по полиморфизму. Я тут изучаю Java (учусь в ИНТУИТ'e) и по ходу дела не совсем уверен, что понял, как реализуется полиморфизм класса. Это как, в классе, что ли, прописывается куча методов, каждый из которых отвечает за создание "своих" объектов? Например, если это графика и класс называется "Shape", то, допустим, один метод создает точку, другой - отрезок прямой линии, третий - отрезок кривой, четвертый - эллипс, пятый - прямоугольник и т.д. Т.е. полиморфизм тесно связан с перегрузкой класса?

И еще просьба привести примеры, где еще применяется полиморфизм (кроме графики).

Изменено пользователем avb_constructor
Ссылка на комментарий
Поделиться на другие сайты

avb_constructor:

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

То есть полиморфизм - изменение содержания метода в зависимости от уровня класса, к которому он применяется.

Ссылка на комментарий
Поделиться на другие сайты

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

А эти методы определены в каждом из этих классов или где-то "на стороне" (в смысле, во внешнем классе)?

Ссылка на комментарий
Поделиться на другие сайты

А эти методы определены в каждом из этих классов или где-то "на стороне" (в смысле, во внешнем классе)?

Пример

class Animal{ Animal() {}; virtual ~Animal(){}; virtual Move() = 0;}class Cat : public Animal{ Cat(){}; ~Cat(){}; Move(){  std::cout << "Cat" };}class Dog : public Animal{ Dog(){}; ~Dog(){}; Move() {  std::cout << "Dog" };}void Do( Animal* a ){  a.Move();}Do ( new Cat ); //отпечатаеть CatDo ( new Dog ); //отпечатаеть Dog

Верность кода не проверял, он для илюстрации

Так более понятно?

Изменено пользователем Darhazer
Ссылка на комментарий
Поделиться на другие сайты

А эти методы определены в каждом из этих классов или где-то "на стороне" (в смысле, во внешнем классе)?
В базовом классе, если у животного нет индивидуального метода передвижения, а если есть, то дополнительно для каждого типа животного в его классе, как показано в примере b]Darhazera[/b]. Где же их еще определять?

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

Поэтому при определении метода Move у Darhazera стоит слово virtual, которое показывает, что при вызове этого метода программе надо по типу реального объекта смотреть, не переопределен ли метод для этого объекта.

Вот еще пример, для простоты без динамических переменных. На C++.

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

#include <cstdlib>#include <iostream>using namespace std;class Animal { public: void virtual Move(); }; class Mouse: public Animal { }; class Eagl: public Animal { public: void Move(); }; class Fish: public Animal { public: void Move(); }; // определили базовый класс Animal и три его потомка, // притом в потомках Eagl и Fish переопределили метод Movevoid Animal::Move() {cout << "Go...\n";} void Eagl::Move() {cout << "Fly...\n";} void Fish::Move () {cout << "Swim...\n";} // определили содержание методов Move в классах Animal, Eagl, Fish// первый печатает "Go...", второй "Fly...", третий "Swim..."int main(int argc, char *argv[]){  Mouse M; Eagl E; Fish F; Animal* pAnimal; // это главная программа, мы создали по экземпляру классов Mouse, Eagl, Fish // и указатель на объект базового класса Animal, // он и будет указывать то на одно животное, то на другоеpAnimal=&E;pAnimal->Move();// было выдано "Fly..." // - у орла свой метод MovepAnimal=&F;pAnimal->Move();// было выдано "Swim..."// - у рыбы тоже свой метод MovepAnimal=&M;pAnimal->Move();// было выдано "Go..."// - мышь за неимением своего собственного пользуется методом базового класса// на какое бы животное не указывал pAnimal, метод Move // берется для типа этого животного, то есть он полиморфный, // а вот если мы уберем в его объявлении в базовом классе слово virtual, // он всегда будет браться для базового типа животного и выдавать Go...system("PAUSE");return EXIT_SUCCESS;}
Ссылка на комментарий
Поделиться на другие сайты

Т.е., если я правильно понял, для каждого вида объектов (например, для графического, типа прямоугольник, многоугольник, отрезок прямой линии, сегмент кривой, эллипс) все-таки нужно создавать свой собственный класс, являющийся потомком базового класса, например, "Фигура" ("Shape")? Но это верно только для С++ или для всех остальных объектно-ориентированных языков (я С++ как-то не стал изучать, а сразу взялся за Яву)? А то в лекции было показано как раз на примере графических примитивов, что для каждого из видов объектов создавать свой собственный класс необязательно. Да и сейчас я, на время оторвавшись немного от изучения Явы, делаю прогу для VBA CorelDraw и обнаруживаю там класс "Shape", и он является перегруженным: в нем создание различных графических примитивов реализовано через методы, входящие в этот самый класс (и никаких подклассов вроде бы в нем нет).

Ссылка на комментарий
Поделиться на другие сайты

avb_constructor:

И смешал Бог языки программистов. Одни заговорили на Java, другие на C++. © :g:

класс "Shape", и он является перегруженным: в нем создание различных графических примитивов

реализовано через методы, входящие в этот самый класс (и никаких подклассов вроде бы в нем нет).

Честно говоря, я вообще не совсем понимаю, что имеется в виду под перегрузкой класса. Перегрузка - вовсе не наличие в классе набора методов для создания разных объектов. Она вообще относится не к классам, а к функциям. Перегружаются функции. Как их частные виды (в широком смысле все это функции) - методы, конструкторы, операторы. Идеология ООП держится на трех китах: наследование, полиморфизм (то есть использование виртуальных функций), инкапсуляция. Но, как говорил апостол Павел, главный из них любовь, то есть наследование. Без наследования все остальное не имеет смысла.

И полиморфизм тут не при чем. Если имеется в виду перегрузка функций, то дело вот в чем: на самом деле это просто хитрый трюк - хотя мы используем функции с одинаковыми именами, но разными списками параметров (сигнатурами), компилятор делает для этих функций разные имена, просто прибавляя к имени функции строку сокращенных названий типов всех параметров. Прием удобный, но никакого отношения к ООП не имеет.

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

А перегрузка функций - самостоятельное средство, ни с ООП, ни с полиморфизмом не связанное. Ее можно использовать в комбинации с ООП, например, перегрузка конструктора класса может быть использована для создания разных вариантов объектов класса. Можно и создать в одном классе несколько методов для создания разных геометричесих фигур, но это не ООП, мне и в обычном Pascal или C, без всякого ООП, никто не мешает создать несколько функций для создания разных геометрических фигур. Или одну функцию с параметром, показывающим тип геометрической фигуры, с которой надо работать функции. Но все эти варианты идут поперек идеологии ООП.

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

Насчет Java и C++: ООП реализуется в них похоже, но все же есть отличия. Например, есть отличия в наследовании перегруженных функций. Но это уже тонкости.

Ссылка на комментарий
Поделиться на другие сайты

avb_constructor

Чтобы использовать полиморфизм - обязательно нужно писать новые классы, т.к. основа полиморфизма - это и есть изменение поведения метода в дочернем классе. Это теория ООП, и от конкретного языка не зависит.

А то в лекции было показано как раз на примере графических примитивов, что для каждого из видов объектов создавать свой собственный класс необязательно.
Приведи пример из лекции, мы его рассмотрим более подробно.
делаю прогу для VBA CorelDraw и обнаруживаю там класс "Shape", и он является перегруженным: в нем создание различных графических примитивов реализовано через методы, входящие в этот самый класс (и никаких подклассов вроде бы в нем нет).
Значит те, кто проектировали класс "Shape" решили, что так будет проще с ним работать. То, что какие-то вещи можно сделать при помощи полиморфизма - не обозначает, что их обязательно нужно делать именно таким образом. В основе той или иной реализации всегда должен лежать здравый смысл и забота об удобстве использования.
Ссылка на комментарий
Поделиться на другие сайты

Народ, я все понял. Да, действительно, надо создавать класс для каждого вида объектов. А еще надо внимательнее лекции читать ;) . (Ну, давно я читал, уже память подвела :) , придется по-новой все читать и более вдумчиво).

В лекции об этом и говорится:

Полиморфизм

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

Предположим, мы хотим создать векторный графический редактор, в котором нам нужно описать в виде классов набор графических примитивов - Point, Line, Circle, Box и т.д. У каждого из этих классов определим метод draw для отображения соответствующего примитива на экране.

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

…//создание пустого массива, который может // содержать объекты Point с максимальным // объемом 1000 Point[] p = new Point[1000];Line[] l = new Line[1000];Circle[] c = new Circle[1000];Box[] b = new Box[1000];…// предположим, в этом месте происходит // заполнение всех массивов соответствующими// объектами…for(int i = 0; i < p.length;i++) {   //цикл с перебором всех ячеек массива.  //вызов метода draw() в случае,  // если ячейка не пустая.  if(p[i]!=null) p.draw();}for(int i = 0; i < l.length;i++) {  if(l[i]!=null) l.draw();}for(int i = 0; i < c.length;i++) {  if(c[i]!=null) c.draw();}for(int i = 0; i < b.length;i++) {  if(b[i]!=null) b.draw();}…

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

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

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

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

…Point p[] = new Point[1000];p[0] = new Circle();p[1] = new Point();p[2] = new Box();p[3] = new Line();…for(int i = 0; i < p.length;i++) {  if(p[i]!=null) p[i].draw();}…

В описанном выше примере массив p[] может содержать любые объекты, порожденные от наследников класса Point. При вызове какого-либо метода у любого из элементов этого массива будет выполнен метод того объекта, который содержится в ячейке массива. Например, если в ячейке p[0] находится объект Circle, то при вызове метода draw следующим образом:

p[0].draw()

нарисуется круг, а не точка.

В заключение приведем формальное определение полиморфизма.

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

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

Рис. 2.3. Пример иерархии классов.

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

void println()			   // переход на новую строкуvoid println(boolean x)	  // выводит значение булевской  // переменной (true или false)void println(String x)	   // выводит строку - значение  // текстового параметра.  

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

Полный текст лекции с иллюстрациями можно прочитать здесь.

Изменено пользователем avb_constructor
Ссылка на комментарий
Поделиться на другие сайты

  • 2 недели спустя...

Что-то не совсем понял суть вопроса.

Я лично как узнал в 9 лет что такое полиморфизм, так и работаю :bye1:

Ссылка на комментарий
Поделиться на другие сайты

Что-то не совсем понял суть вопроса.

Я лично как узнал в 9 лет что такое полиморфизм, так и работаю :)

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

Так что у каждого свой опыт в программировании. ;)

Изменено пользователем avb_constructor
Ссылка на комментарий
Поделиться на другие сайты

  • 1 год спустя...

Понять не могу, что такое конструктор и для чего он нужен. В книге написано, что инициализирует объект... Но что-то абстрактно...

При том, из примера, он похож на функцию... Растолкуйте пожалуйста... :g:

Ссылка на комментарий
Поделиться на другие сайты

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

Есть также и деструкторы. Деструктор вызывается, чтобы уничтожить экземпляр класса и освободить память. Это тоже функция, как и конструктор, ее имя отличается от имени конструктора специальным значком впереди имени.

P.S. "написано, что он инициализирует объект". Объекты - это то, что я называл экземплярами класса. Конструктор при вызове может не только создать экземпляр класса, но и сразу присвоить полям данных этого объекта какие-то начальные значения - инициализировать объект. Поэтому, кстати, у класса могут быть несколько конструкторов. Они должны отличаться списками параметров, иначе компилятору было бы не отличить, какой конструктор вызывать - имена-то у всех конструкторов класса одинаковые.

Изменено пользователем Тролль
Ссылка на комментарий
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

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

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...