Модераторы: Snowy, MetalFan, bems, Poseidon

Поиск:

Закрытая темаСоздание новой темы Создание опроса
> Зачем нужен virtual – override, Ведь методы и без того замещаются 
V
    Опции темы
bems
Дата 31.12.2009, 17:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 3400
Регистрация: 5.1.2006

Репутация: 18
Всего: 88



ну у потомка же нет собственного метода DoTest, поэтому в любом случае вызывается унаследованный от предка. А внутри него вызов Test (если убрать виртуальность) означает вызов из того же класса - остальные там просто не видны.




--------------------
Обижено школьников: 8
PM MAIL   Вверх
PsiMagistr
Дата 31.12.2009, 18:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 479
Регистрация: 31.12.2009

Репутация: 1
Всего: 1



Таким образом на основании двух примеров формируем след вопрос:

Пример первый. Описание классов от bems

Код


//Родительский

 type TMyClassA=class
  procedure Test; virtual;
  procedure DoTest;
  end;

//Дочерний

type TMyClassB=class(TMyClassA)
  procedure Test; override;
  end;



//Реализация методов


procedure TMyClassA.Test;
begin
  ShowMessage('Вызов родительского метода');
end;

procedure TMyClassA.DoTest;
begin
  ShowMessage('TMyClassA.DoTest');
  Test
end;

procedure TMyClassB.Test;
begin
  ShowMessage('Вызов дочернего метода');
end;



//Обработчик на кнопке

procedure TForm1.Button1Click(Sender: TObject);

var a:TMyClassB; // Обратите внимание как бы мы не объявляли переменную-указатель от предка или от потомка, для замещения родительского метода дочерним требуются директивы virtual-override  


begin
a:=TMyClassB.Create;
a.DoTest;
a.Free;
end;




Пример второй


Код


//Родитель

type TMyClassA=class
  procedure DoTest; Virtual; 
  end;

//Наследник

type TMyClassB=class(TMyClassA)
procedure DoTest; override;
 end;


Реализация


procedure TMyClassA.DoTest;
begin
  ShowMessage(' Работает родительский метод'); 
  end;


procedure TMyClassB.DoTest;
begin
  ShowMessage('Работает дочерний метод');
end;

// Обработчик кнопки

procedure TForm1.Button1Click(Sender: TObject);

var a:TMyClassB; //Если переменная-указатель объявлена от класса потомка, происходит замещение родительского метода дочерним без(!) директив. Если указатель объявлен от класса предка необходимо прописывать virtual-ovrride


begin
a:=TMyClassB.Create;
a.DoTest;
a.Free;
end;



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

bems, Alexsis спасибо большое за примеры.  Bems, Насчет вызова родительского метода у дочернего класса я понял, собственный у него отсутствует, он пользуется тем что есть (унаследованным).










Это сообщение отредактировал(а) PsiMagistr - 31.12.2009, 19:12


--------------------
"Арфы нет? Возьмите бубен!

Ребята, будем жить!"

 (с) "В бой идут одни старики"

---

"ИЕ" - один из самых сумасшедших браузеров в нашей галактике.
PM MAIL   Вверх
bems
Дата 31.12.2009, 20:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 3400
Регистрация: 5.1.2006

Репутация: 18
Всего: 88



Цитата(PsiMagistr @  31.12.2009,  18:34 Найти цитируемый пост)
Т.е. я полагал, что без этих директив, род. методы не должны перекрываться по любому
Они и не перекрываются. Но когда ты объявляешь у дочернего класса метод, который уже есть у родителя, то дочерний скрывает родительский. И компилятор честно об этом предупреждает ворнингом. Когда ты видишь этот ворнинг, ты должен подумать что же ты хотел на самом деле. Если забыл указать override, то укажи. Если действительно хочешь скрыть старый метод, то укажи reintroduce



--------------------
Обижено школьников: 8
PM MAIL   Вверх
PsiMagistr
Дата 1.1.2010, 11:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 479
Регистрация: 31.12.2009

Репутация: 1
Всего: 1



Bems, большое спасибо, вы мне просто глаза открыли, честное слово. smile
 
Таким образом:

Существует родительский класс "А" и класс-наследник "В"

В классе "А" прописан некоторый метод C-metod, вызывающий в процессе выполнения дополнительно метод D-metod того же класса.

Класс наследник В обладает собственным D-metodom, отличным по исполнению от родительского. Но собственным методом С-metod, класс В не обладает. Этот метод переходит к нему по правам наследования.

Если объекту класса B необходимо исполнить унаследованный С-metod, метод D-metod, вызываемый внутри такого С-metod  необходимо сделать виртуальным (Virtual) в объявлении класса родителя, а затем переопределить (override), в объявлении  потомка, иначе будет выполнен D-metod класса А.

Т.е. пара virtual-override используется, когда когда наследуемому  методу необходимо выполнить чужеродные ему по изначальной природе элементы (методы дочерних классов).

Будь у класса B собственный метод С-metod, он бы просто сокрыл родительский (пропиши rentroduce), чтобы компилятор не ругался.  


Это сообщение отредактировал(а) PsiMagistr - 1.1.2010, 11:53


--------------------
"Арфы нет? Возьмите бубен!

Ребята, будем жить!"

 (с) "В бой идут одни старики"

---

"ИЕ" - один из самых сумасшедших браузеров в нашей галактике.
PM MAIL   Вверх
bems
Дата 1.1.2010, 18:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 3400
Регистрация: 5.1.2006

Репутация: 18
Всего: 88



Ничего не понял smile

Добавлено через 38 секунд
Не ты. Я ничего не понял в твоём посте


--------------------
Обижено школьников: 8
PM MAIL   Вверх
PsiMagistr
Дата 1.1.2010, 18:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 479
Регистрация: 31.12.2009

Репутация: 1
Всего: 1



Попробую объяснить на примере твоего же кода, боюсь выйдет некоторый флуд, но я удалю потом:



Существует родительский класс TMyClassA и класс-наследник TMyClassB. 

В классе  TMyClassA   прописан некоторый метод DoTest, вызывающий в процессе выполнения, кроме всего прочего свой метод Test.

Класс наследник TMyClassB обладает собственным методом Test отличным по исполнению от родительского Test. Но собственным методом DoTest класс TMyClassB не обладает. Этот метод переходит к нему по правам наследования.

Если объекту класса TMyClassB необходимо исполнить унаследованный  DoTest , метод Test, вызываемый внутри такого DoTest необходимо сделать виртуальным (Virtual) в объявлении класса родителя, а затем переопределить (override), в объявлении  потомка, иначе будет выполнен Test TMyClass.Test, вместо TMyClassB.Test .

Т.е. пара virtual-override используется, когда когда наследуемому методу необходимо выполнить чужеродные ему по изначальной природе элементы (методы дочерних классов).

Будь у класса  TMyClassB  собственный метод DoTest, он бы просто сокрыл родительский (пропиши rentroduce), чтобы компилятор не ругался.





Это сообщение отредактировал(а) PsiMagistr - 1.1.2010, 19:47


--------------------
"Арфы нет? Возьмите бубен!

Ребята, будем жить!"

 (с) "В бой идут одни старики"

---

"ИЕ" - один из самых сумасшедших браузеров в нашей галактике.
PM MAIL   Вверх
PsiMagistr
  Дата 3.1.2010, 13:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 479
Регистрация: 31.12.2009

Репутация: 1
Всего: 1



Продолжаю изучать теорию ООП. Изваял  микропрограмму, использующую классы и Virtual-override директивы.

Программа направлена на вычисление объема геометрических фигур: призмы с квадратным основанием и призмы с круглым основанием (цилиндр).  Согласно математике объем данных фигур можно получить по общей формуле: Объем:= ПлощадьФигуры*ВысотуФигуры. Поэтому метод GetVolume (Получение объема) является общим для всех классов. Однако этот общий метод использует метод GetArea (ПолучениеПлощади), реализация которого является различной для дочерних классов "Круг" (TDisk) и "Квадрат"  (TSqr), поэтому метод GetArea объявлен виртуальным. 


Переходим в раздел глобальных переменных и опишем класс родоначальник:



Код


 type Figura = class

// Поля

  fName:String; // Поле, отражающее название фигуры.
  fHeigt:Real; //  Поле, отражающее высоту фигуры.
  fRadiusOrSize:real; // Поле, отражающее радиус круга или сторону квадрата.

//Методы

Constructor Create (fn:String; fh:Real; fr:Real); //Трехпараметральный Конструктор. Используется при инициализации полей.
 
  Function GetArea : Real; Virtual;  // Метод получения площади фигуры, разнится для всех классов, однако используется в общем методе GetVolume и поэтому перекрываемый.

 Function GetVolume:Real; // Метод получения объема, одинаков для всех. 

 end; //Конец описания класса

// Опишем класс наследник Квадрат.

type TSqr = class(Figura) //Наследник класса Figura. Наследует поля, конструктор и метод получения объема. 

Function GetArea:real;override; //Метод получения площади перекрывается.

end;


//И класс-наследник Круг.

 type TDisk = class(Figura)   //Наследник класса Figura. Наследует поля, конструктор и метод получения объема. 

 Function GetArea : real;override;//Метод получения площади перекрывается.


end;

//Перейдем в область реализации методов. (inplementation)

Constructor Figura.Create; //Реализация Конструктора класса Figura. Метод служит для выделения памяти и инициализации полей.

begin

fName:=fn;
fHeigt:= fh;
fRadiusOrSize:=fr;

end;


Function Figura.GetArea;  //Реализация метода GetArea класса Figura

Begin

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

//GetArea:=0;

// В данном случае что именно вернет функция - неважно. (тип Real) Данная реализация никогда не будет использована.

end;

Function Figura.GetVolume;  //Реализация метода GetVolume класса Figura

begin

GetVolume:= GetArea*FHeigt; //Получаем объем призмы. Обратите внимание на использование в расчете метода GetArea.

end;



Function TDisk.GetArea; //Реализация метода GetArea, класса TDisk (Круг)

begin

GetArea := 3.141592 * fRadiusOrSize*fRadiusOrSize; //Получаем площадь круга.

end;


Function TSqr.GetArea; //Реализация метода GetArea,  класса TSqr (Квадрат)

begin

GetArea:= fRadiusOrSize*fRadiusOrSize; //Получаем площадь квадрата.


end;

// Классы подготовлены к использованию. Теперь осталось это дело запустить.


// Добавьте на форму две кнопки (Button1, Button2),  два текстовых поля (Edit1, Edit2) для задания фактических параметров Высоты и РадиусаСтороны и надпись (Label1). Измените Button1.Caption на слово "Квадрат" и запишите  обработчик.


procedure TForm1.Button1Click(Sender: TObject);

var a:TSqr; // Создание переменной типа TSqr "Квадрат"

begin 

 a:= TSqr.Create('Квадрат', StrToFloatDef(Edit1.Text,0), StrToFloatDef(Edit2.Text,0)); // Создание объекта. Ключ на старт.

Label1.Caption := a.FName + ' ' + FloatToStr(a.GetVolume); //Объект в работе.

a.Free; // Прощай объект. 

// и для второй кнопки.


procedure TForm1.Button2Click(Sender: TObject);

var a: TDisk; // Создание переменной типа TDisk "Круг"

begin

a:= TDisk.Create('Круг', StrToFloatDef(Edit1.Text,0), StrToFloatDef(Edit2.Text,0)); //Создание объекта. Ключ на старт.


Label1.Caption := a.FName + ' ' + FloatToStr(a.GetVolume); // //Объект в работе. 

a.Free; // Прощай объект. 

end;

end;





Разработано и тестировано мною в среде Дельфи 6.

Попробуйте убрать Virtual-override, раскомментировать реализацию GetArea для класса Figura и посмотреть, что получится. 

smile 


Это сообщение отредактировал(а) PsiMagistr - 3.1.2010, 17:43


--------------------
"Арфы нет? Возьмите бубен!

Ребята, будем жить!"

 (с) "В бой идут одни старики"

---

"ИЕ" - один из самых сумасшедших браузеров в нашей галактике.
PM MAIL   Вверх
bems
Дата 3.1.2010, 15:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 3400
Регистрация: 5.1.2006

Репутация: 18
Всего: 88



Для случаев вроде Figura.GetArea нужно использовать abstract и убрать пустую реализацию вообще


--------------------
Обижено школьников: 8
PM MAIL   Вверх
PsiMagistr
Дата 3.1.2010, 15:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 479
Регистрация: 31.12.2009

Репутация: 1
Всего: 1



Bems, а что за команда Abstract, объясни нубику, плиз, а то я только 3 дня, как пытаюсь понять ООП. 


--------------------
"Арфы нет? Возьмите бубен!

Ребята, будем жить!"

 (с) "В бой идут одни старики"

---

"ИЕ" - один из самых сумасшедших браузеров в нашей галактике.
PM MAIL   Вверх
bems
Дата 3.1.2010, 16:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 3400
Регистрация: 5.1.2006

Репутация: 18
Всего: 88



в объявлении класса Figura метод GetArea объявляешь как
Код

Function GetArea : Real; Virtual; abstract;

Его пустую реализацию удаляешь вообще. Дочерние классы не меняются


--------------------
Обижено школьников: 8
PM MAIL   Вверх
PsiMagistr
Дата 3.1.2010, 16:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 479
Регистрация: 31.12.2009

Репутация: 1
Всего: 1



Bems, благодарю. Ты мне очень-очень сильно помогаешь.

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

Насколько теперь я могу понимать, если б классы TDisc и TSqr имели свою реализацию GetVolume, а не наследованную родительскую, это было б не перекрытие (override), а просто сокрытие, так?



А в чем различие между override и overload?



Это сообщение отредактировал(а) PsiMagistr - 3.1.2010, 16:30


--------------------
"Арфы нет? Возьмите бубен!

Ребята, будем жить!"

 (с) "В бой идут одни старики"

---

"ИЕ" - один из самых сумасшедших браузеров в нашей галактике.
PM MAIL   Вверх
bems
Дата 3.1.2010, 16:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 3400
Регистрация: 5.1.2006

Репутация: 18
Всего: 88



ну вот тут же ты вызываешь
Код

GetVolume:= GetArea*FHeigt
и оно работает, до тех пор, пока вызвается на объекте дочернего класса (не важно, какого типа ссылка, главное чей конструктор вызывался)
Но да, если вызвать это на самом предке, то будет ошибка EAbstractError
И это очень хорошо smile
Потому что это в обоих случаях ошибка, но при Abstract тебе на нее покричат, а без Abstract прога просто будет работать не так как надо (потому что в лоюбом случае не научена она считать объем какой-то неконкретной абстрактной финуры)


Цитата(PsiMagistr @  3.1.2010,  16:24 Найти цитируемый пост)
А в чем различие между override и overload?
А почти ничего общего. И к ООП не относится. И один топик - один вопрос.



--------------------
Обижено школьников: 8
PM MAIL   Вверх
PsiMagistr
Дата 4.1.2010, 13:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 479
Регистрация: 31.12.2009

Репутация: 1
Всего: 1



А какова практическая разница между Virtual и Dynemic?


--------------------
"Арфы нет? Возьмите бубен!

Ребята, будем жить!"

 (с) "В бой идут одни старики"

---

"ИЕ" - один из самых сумасшедших браузеров в нашей галактике.
PM MAIL   Вверх
bems
Дата 5.1.2010, 00:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 3400
Регистрация: 5.1.2006

Репутация: 18
Всего: 88



Практической нет. Dynamic занимает меньше памяти при некоторых условиях, но работает немного медленней. Но это всё не заметно на практике. Используй dynamic там где точно знаешь что без него никак, и virtual во всех остальных случаях.
И еще: методы обработки сообщений (те что с директивой message) на самом деле динамические, хоть объявляются без dynamic


--------------------
Обижено школьников: 8
PM MAIL   Вверх
PsiMagistr
Дата 14.1.2010, 12:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 479
Регистрация: 31.12.2009

Репутация: 1
Всего: 1



Ребята, я прошу прощения за некоторый оффтоп, столкнулся со следующей задачей:

Есть несколько картинок (класс TImage) с изображениями в них. При нажатии на какую либо картинку, внешний вид ее должен меняться, показывая, что картинка выбрана. Когда же мы нажимаем на след. картинку, выбирается она, а первая возвращается в исходное положение. Таким образом выбрать можно только одну картинку, плитки как бы переключаются между собой.

Как возможно решить данный вопрос поизящнее?


--------------------
"Арфы нет? Возьмите бубен!

Ребята, будем жить!"

 (с) "В бой идут одни старики"

---

"ИЕ" - один из самых сумасшедших браузеров в нашей галактике.
PM MAIL   Вверх
Страницы: (3) Все 1 [2] 3 
Закрытая темаСоздание новой темы Создание опроса
Правила форума "Delphi: Для новичков"
SnowyMetalFan
bemsPoseidon
Rrader

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.1141 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.