видимостью и доступом при наследовании.




class Base {

void f();

};

 

class Derived: public Base {

int f;

};

1)перегрузке(overloading) (в одной области действия)

2)скрытии(hiding)

3)переопределении(overriding) (динамическое связывание - рассмотрим чуть – чуть позднее)

Перегрузка функций(! Именно функций, так как не существует перегрузки свойств)

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

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

Скрытие: скрытие происходит при наследовании, в унаследованном классе, если в производном классе есть такое же имя как в базовом.

Для того, чтобы ращличать две этих N, используются ключевые слова super, base, inherited:

super.N;

base.N;

inherited.N;

(Эти слова очень удобны, они позволяют держать в глове имя базового класса)

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

Запрещение наследования для классов и методов.

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

В Java можно запретить наследование с помощью ключевого слова final. Если это слово стоит перед именем метода, то этот метод не может переопределяться в производных классах. Если же оно стоит перед определением класса, то класс нельзя наследовать.

В C# такую роль играет sealed (там sealed имеет смысл ставить только около виртуальных методов). Sealed может стоять или перед определением, или перед замещением виртуального метода.

Любой сатический класс в C#3.0 запечатан.

Наследование и специальные функции. Понятие о множественном

наследовании.

Как было сказано выше из всех проходимых нами языков, множественное наследование реализовано только в С++, поэтому примеры ниже будут написаны на С++

синтаксис.

class A {... };

class B {... };

class C: public A, public B {... };

Базовый класс не может появиться несколько раз в этом списке явно (ситуация ‘одна база – два раза’). Однако

может возникнуть такая ситуация:

class L { public: int n;... };

class A: public L {... };

class B: public L {... };

class C: public A, public B {... };

Например, мы напишем: C.c; c.n = 0; Возникает неоднозначность: какую c нам вызвать, ту которая пришла к нам через A, или же ту, которая наследовалась через B. Здесь мы можем уточнить: c.A::n = 5; или c.B::n = 7;. Перед оператором разрешения контекста мы указываем точку, от которой начинается поиск переменной.

Замечание: в других языках тоже существует оператор уточнения (x::N ~ super.N ~ base.N ~ inherited.N)

10. Динамический полиморфизм

Статическое и динамическое связывание методов. Динамический тип

данных и динамическое связывание. Замещение функций и динамическое

связывание. Особенности динамического связывания в современных ЯП.

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

Механизм виртуальных функций.

• Если вызываем метод через объект (h.print()), то виртуальность не работает, статическое определение по типу объекта, от которого его вызываем.

• При явном указании класса виртуальность не работает, даже если функция вызвана через указатель, то есть pp->Person::print(); – статическое определение.

С#, Delphi

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

Ключевое слово override означает, что метод, в объявлении которого оно появляется, является заместителем виртуального метода из базового класса.

ADA 1995

Если есть параметр tегированного типа, для вызова есть специальные типы:

CWT – class wide types – классовый тип

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

type T is tagged record …… end

T – класс, Т’ – классовый тип

type A is array (index range <>) of T;

X:A; //-ошибка!

X: A (range L..R); //нормальноJ

X:T’

Тçprocedure P (X: T);

T1çprocedure P (X: T1);

Представим, что существует некоторая глобальная процедура

procedure CALL(A: T); // P(A); -- P(X:T)

procedure CALLV (A: T’class) //P(A);

Тогда:

X: T;

Y:T1;

CALL(X); // - P(T)

CALL(Y); // - P(T)

CALLV(X); // - P(T)

CALLV(Y); // - P(T1)

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

Оберон -2 отличается от Оберон тем, что в нем есть процедуры и динамическое приведение к типу – по определению аналог виртуальных методов.

Пример

TYPE T RECORD
……………………..

END;

 

TYPE T1 RECORD(T)
……………………..

END;

PROCEDURE(VAR X: T) P; //виртуаьная функция, Х передается как ссылка

Перекрытия нет, а замещение есть:

PROCEDURE(VAR X: T1) P; //динамическая(виртуальная) функция

VAR X: T;

Y: T1;

X.P; // -------------P(T)

Y.P; // --------------P(T1)

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

PROCEDURE CALL (VAR A: T); //обычная функция

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

Достоинства и недостатки динамического связывания. Снятие

динамического связывания. Механизм реализации динамического

связывания на примере языка Си++. Таблица виртуальных методов.

Понятие о мультиметодах.

Вообще говоря, Ада наиболее близко подошла к концепции МУЛЬТИМЕТОДА (метода, связанного по нескольким параметрам). Тем не менее, из соображений эффективности он в ней не реализован.

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

CALL_W(X:T’ class, Y: W’ class);

P(X, Y);

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

Мультиметоды есть, например, в языке CLOS(Common Lisp With Object Systems) – достаточно известный язык в довольно узких кругах.

11. Абстрактные классы и интерфейсы

Понятие абстрактного класса (АК). Необходимость понятия АК при

проектировании иерархий классов. Воплощение концепции АК в

современных ЯП.

Абстрактные классы и интерфейсы. Интерфейс как языковая

конструкция. Связь интерфейсов и других языковых конструкций

(итераторов, сохраняемых объектов и т.д.). Интерфейсы и иерархии классов.

Сами по себе иерархии классов бесполезны, если в них нету динамически связанных методов.

• Если существуют признаки, общие для всех объектов в класса иерархии, то эти признаки целесообразно поместить в базовый класс

• У каждого объекта класса имеется состояние (текущие значения параметров) и поведение (методы класса)

• Некоторая функциональность (особенности поведения), общая для всех классов в иерархии, может быть реализована только при дальнейшей детализации в производных классах.

=> приходим к понятию абстрактного базового класса

В языках Delphi, Оберон-2, TP 5.5, C++, Java, C# имеется языковая поддержка абстрактных классов.

Пример:

Figure – абстрактная фигура

Общие данные – (x,y) – координаты фигуры

Общие методы – Draw(), Move() – не могут быть реализованы для абстрактной фигуры

Что будет если просто не определить витуальную функцию в базовом классе:

Полиморфный класс – класс, в котором есть виртуальные функции (и. следовательно, таблица виртуальных функций)

class X

{

public:

virtual void f(); //Объявлена, но не определена

void g();//Объявлена, но не определена

}

class Y: public X

{

public

virtual void f() {…;}//Замещена

}

….

X * py = new Y(); // уже здесь компилятор выдаст ошибку из-за того, т.к. непонятно что записывать в таблицу виртуальных функций

py -> f();

X a; // здесь компилятор также выдаст ошибку из-за того, из-за того, что не сможет заполнить таблицу виртуальных функций. Если бы функция X:: f() не была виртуальной, то ошибки здесь не было бы.

a.g();// ошибка, т.к X::g() не определена

Решение проблемы – языковая поддержка

В C#, Java:

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

abstract class base //Абстрактный класс

{

abstract public void Draw(); // Функция без реализации

}

С#

class D: base{
public override void draw(){……};

};

 

Java

class D: extends base{
public override void draw(){……};

};

//обязательно поставить тело Draw!

В C++:

Чисто виртуальная функция (абстрактная) – virtual «прототип» = 0;

В Аде:

procedure P(X: T) is abstract;

где T – тегированный тип.

Объекты абстрактных классов нельзя создавать.

При вызове виртуальной функции в конструкторе виртуальность вызова снимается.

Существует метод, который не должен быть чисто виртуальным – деструктор. Он всегда должен быть виртуальным и реализованным.

Различие между абстрактными классами и абстрактными типами данных:

В абстрактных классах абстрагируются от реализаций некоторых методов. В абстрактных типах данных абстрагируются от всей структуры.

Абстрактный тип данных (АТД) — это тип с полностью инкапсулированной структурой. Использовать объекты АТД возможно только при помощи явно определенных в интерфейсе типа операций.

Абстрактный класс (АК) — это класс, содержащий хотя бы один абстрактный метод.

class Iset

{

virtual void include(const T &) = 0;

virtual exclude(const T &) = 0;

«статические члены»

}

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

Множественное наследование интерфейсов. Реализация интерфейсов и ее

особенности современных ЯП. Явная и неявная реализация интерфейсов.

В C# и Java, в отличие от C++, существует языкового понятия интерфейса:

interface «имя»

{

«объявления членов»

}

Внутри интерфейса могут быть статические поля. Поля без «static» будут восприняты как статические. Также членами интерфейса могут быть методы и свойства, которые считаются чисто виртуальными и публичными.

Т.е. интерфейс – чистый контракт, не реализующий структуру.

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

Если написать

int pi{gte; set}

то оно превратится в свойство.

Кстати, перед определением свойства может стоять слово virtual. То есть свойства могут быть виртуальными или чисто виртуальными.

В Java вместо get и set есть getpair и setpair.

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

C#

class D: имя, интерфейсы:

Java

Class D extends Base implements именя интерфейсов

Интерфейсы могут содержать внутри себя любые вложенные классы.

В С# появляется так называемая явная и неявная реализация интерфейсов.

Неявная реализация – это то, что мы всегда называем обычной реализацией.

Пример неявной реализации интерфейсов:

interface ISample{

void f(); };

class CoClass: ISample{

public void f(){………………..}; //если тут не поставить public, компилятор заругается.

};

Явная реализация интерфейсов:

class CoClass2: ISample{

void ISample f() {…………….}

//тут спецификатор public отсутствует, потому что попытка написать тут public карается

};

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

D * px;

px->f(); //непонятно, какой метд я хочу вызывать – ошибка.

Но px->I1::f(); //снова ошибка: попытка вызова чисто виртуальной функции.

Мы же хотим вызвать заместителя для I1. Это длается так:

((I1*)px)->f();

Замечание. Слова «явный» и «неявный» должны относиться к приведению типов, а не к классам(интерфейсам).

CoClass2 x;

(ISample)x.f();//явное приведение

При явной реализации вызов возможен лишь ри явом приведении.

Явная реализация интерфейса означает, что вызов метода

интерфейса может происходить только через ссылку на интерфейс, но неможет происходить через ссылку на класс, реализующий интерфейс. Перед

вызовом интерфейсного метода необходимо явно преобразовать ссылку на

объект реализующего класса к ссылке на интерфейс. Концепция явной

реализации полезна, например, при конфликте имен между унаследованными

интерфейсами.

В C# реализованные методы интерфейсов считаются по умолчанию «sealed» - запечатанными. В наследниках они перекрываются.

class ExampleClass: IControl

{

public void Paint(){ …;}

}

class FancyComBox: ExampleClass

{

public void Paint(){….; } // Компилятор выдаст предупреждение для этой строчки, в котором сообщит, что «FancyComBox.Paint() скрывает унаследованный метод ExampleClass.Paint(). Используйте ключевое слово «new», если это сделано целенаправленно. Т.е. если поставить «new» перед определением, то предупреждение исчезнет.

Замечание2. Интерфейсы-маркеры.Реализованы в Ада. Являюются разновидностью стандартных интерфейсов, относящихся к категории ссылочных типов. Цель маркеров состоит в том, чтобы вызов через интерфейс был равносиелн по эффективности вызову виртуального метода.

Java: Iterable.

Интерфейсы-маркеры – это стандартные интерфейсы, доведенные до абсолюта. Контракт интерфейсов-маркеров описан только в документации. Это пустые интерфейсы, мы не можем увидеть их с своем коде.

Пример: интерфейс Cloneable – класс, поддерживающий этот интерфейс, обязан реализовать метод Clone

12. Множественное наследование

Множественное наследование в языке Си++. Ромбовидное

наследование и его примеры. Проблемы множественного наследования:

конфликт имен, реализация динамического связывания. Особенности



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-02-16 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: