лекции (2008) (Фингеров Александр_ Кононов Алексей_ Кузин Сергей), страница 16
Описание файла
Документ из архива "лекции (2008) (Фингеров Александр_ Кононов Алексей_ Кузин Сергей)", который расположен в категории "". Всё это находится в предмете "языки программирования" из 7 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Онлайн просмотр документа "лекции (2008) (Фингеров Александр_ Кононов Алексей_ Кузин Сергей)"
Текст 16 страницы из документа "лекции (2008) (Фингеров Александр_ Кононов Алексей_ Кузин Сергей)"
->Rectangle
->Circle
virtual void Draw(); в классе фигура — наследовать не обязательно
Но допустим, что Rect->PatternRect и есть список из фигур.
Нарисовать все фигуры:
while (L!=NULL){
L->f->Draw();
L=L->next;
}
Заметим, что если мы вводим свой новый класс, то переписывать ничего не нужно. Чтобы добавить новый ТД нужно просто унаследоваться и переопределить соответствующие методы.
void PatternRect::Draw()
{
Fill(pattern);
Rect::Draw();
}
Заметим, что Rect::Draw() — не простой вызов. Если убрать Rect::, то будет рекурсия. Виртуальная функция — функция, все вызовы которой динамически связаны.
Приватность определения функции в С++ и C# означает, что мы просто запрещаем непосредственно вызывать нашу реализацию. Либо мы целиком меняем реализацию, либо терпим. В С++ отменить виртуальность метода мы не можем, в Java тем более, в C# и Delphi ситуация другая.
Лекция 30.
Метод называется виртуальным, если ему предшествует ключевое слово virtual.
Метод (функция) является замещающим, если у него совпадает имя, сигнатура и ковариантный тип возвращаемого значения (если тип возвращаемого значения T, то тип T1<=T (с точки зрения наследования) (T f() , T1 f() - замещающая)).
class Base{
public:
virtual Base* Clone();
};
class Derived:public Base{
Derived* Clone(); (замещающая и виртуальная, так как в базовом виртуальная)
}
Base * pb = new Base;
pb->clone();
Derived * pd = new Derived;
pd->clone(); //нельзя
pb = pd;
pb->clone(); //Ok
Это все было С++, теперь рассмотрим, как обстоят дела в Java.
X:
public void f() ()
class Y extends X{
private void f() ()
}
Если попробовать вызвать функция f(), то будем вызываться функция класса X, а функцию в классе Y можно вызвать только в том случае, если вызов будет внутри класса Y.
Ключевое слово final – запрещает динамическое замещение (это является единственным способом запретить его). В Java любой метод всегда виртуально перекрывает метод базового класса с теми же именем и сигнатурой, если он есть.
Определение типа функций:
-
не совпадает имя – новая функция
-
совпадает имя, а профиль нет – скрытие
-
совпадает имя, профиль, а типы не ковариантные – то ошибка
Также существует модификатор override.
class Y::X{
public override void f() {};
};
Если ключевого слова override нет, то свойство динамического связывания теряется. (виртуальности)
В Delphi.
type X = class
procedure P; virtual;
end;
type Y = class (X)
procedure P; override;
end;
Ситуация аналогична С#. В C# выдается предупреждение, если нет ключевого слова override или new в определении функции.
Class Z: Y{
public virtual void f();
};
Y y = new Z();
y.f(); //вызывается функция из класса Z, так как в нем указано ключевое слово virtual.
Возможна ситуация, когда функция не была виртуальной в базовом классе, но стала таковой в классе потомке.
Оберон.
TYPE FIGURE* = RECORD
…
END;
TYPE LINE* = RECORD (FIGURE)
…
END;
TYPE CIRCLE* = RECORD (FIGURE)
..
END;
PROCEDURE DRAW (VAR F:FIGURE); (VAR X: LINE)
IF F IS LINE THEN
DRAWLINE (LINE.F);
ELSEIF F IS CIRCLE THEN
DRAWCIRCLE(CIRCLE.F);
Рассмотрим пример с динамической привязкой типа.
PROCEDURE (VAR F: FIGURE) DRAW();
PROCEDURE (VAR C: CIRCLE) DRAW();
Это единственный случай, когда можно написать функции с одинаковым именем в одной области видимости.
PROCEDURE DRAWALL (VAR F.FIGURE);
…
F.DRAW();
(
В псевдомодульном определении будут сгенерировано следующее:
TYPE FIGURE =
RECORD
ОБЪЯВЛЕНИЕ ВСЕХ ВИДИМЫХ ЧЛЕНОВ
PROCEDURE DRAW();
END;
DRAW^ () – вызов унаследованной реализации.
Мультиметод.
FIGURE INTERSECT (F1, F2: FIGURE);
Как ее вызвать?
Можно было придумать так f1@f2.intersect. Это как бы две ссылки this – слева и справа.
Но в рассматриваемых нами языках мультиметодов нет, хотя есть экспериментальные языки, которые это поддерживают.
Ада-95.
type Base is tagged record … end record;
type Derived is new Base with record <новые члены и объявления> end record;
procedure P(x:Base);
procedure P(y:Derived);
Нет замещения или динамической привязки.
b : Base;
d : Derived;
P(b); {P(Base)}
P(d); {P(Derived)}
P(Y:Base); {P(Base)}
В Ада-95 были введены CW (class wide) – переменные и CW-типы.
T – tagged word
T’ class – объединение T и всех объектов, производных от него.
X : Base’ class – полный аналог неограниченных переменных. Такой переменной можно присвоить любой объект класса Base или производных от него.
P(X) – динамический вызов
P(b) ; static
P(d); static
P(X); dynamic
FIGURE -> LINE
FIGURE -> CIRCLE
procedure DrawAll (p: Figure’class)
Любая процедура, у которой в качестве параметра есть хотя бы один тегированный тип, может быть вызвана динамически, причем тегированный объект должен быть заменен на объект конкретного типа.
function Intersect (X, Y : Figure) return Figure;
function Intersect (A, B: Figure class) return Figure; //плохо
Но только один параметр может быть классового типа и не более. Но мы можем написать
Intersect(X, L) или Intersect (C, X), то есть указывая один классовый параметр.
class Figure{
public:
virtual void Draw();
};
class circle: public Figure{
public:
void Draw();
};
Такая реализация неверна, так как надо описать Figure::Draw();. Класс Figure является абстрактным классом. Понятие абстрактного класса есть во всех ООЯП.
В С++ есть понятие чисто виртуальной функции, а класс, содержащий хотя бы одну такую функция является абстрактным. Нельзя создать объекты абстрактного класса. Например:
virtual void Draw() = 0; (функция может и не иметь реализацию)
Figure f; //[-]
Figure *p; //[+]
p = new Figure; //[-]
p = 0; // [+]
p = new Line; //[+]