лекции (2015) (1160856), страница 12
Текст из файла (страница 12)
Экземпляры абстрактного класса нельзя создавать, но можно (и нужно)использовать ссылки на абстрактный класс.Интерфейс класса – совокупность открытых членов класса.АК + АТД → “интерфейс”(если абстракция ото всех реализаций всех методов){только метод-деструктор - виртуальный, но нельзя чисто, т. к. компилятор сам вставляетневиртуальный вызов деструктора без класса в производном}С++:сlass Y: <модификатор наследования> X {...}class Y: private X {} // часто бывает60Пример. Множество.class ISet// <-- абстрактный класс{virtual void Ird (const T& x) = 0;virtual void Rxd (const T& x) = 0;virtual bool IsInt (const T& x) = 0;virtual ~ISet() {}}class BitSet: public ISet, private BitScale {...}// соответствующие методы ISet вызывают методы BitScale// реализация делегированияclass TreeSet: public ISet, private RBTree {...}+++дополнительно сводим затраты на перекомпиляцию в min, т.
к. достаточноперекомпилировать реализацию без клиентовprivate наследование позволяет инкапсулировать (скрыть) внутреннюю структуру класса.public наследование интерфейса необходимо.Различия между абстрактным классом и интерфейсом в Java (на англ)●●●●●●●All methods in an interface are implicitly abstract. On the other hand, an abstract class may containboth abstract and non-abstract methods.A class may implement a number of Interfaces, but can extend only one abstract class.In order for a class to implement an interface, it must implement all its declared methods.
However,a class may not implement all declared methods of an abstract class. Though, in this case, the subclass must also be declared as abstract.Abstract classes can implement interfaces without even providing the implementation of interfacemethods.Variables declared in a Java interface is by default final. An abstract class may contain non-finalvariables.Members of a Java interface are public by default. A member of an abstract class can either beprivate, protected or public.An interface is absolutely abstract and cannot be instantiated. An abstract class also cannot beinstantiated, but can be invoked if it contains a main method.В C# и Java множественное наследование может быть только по интерфейсам!interface имя {сигнатуры методов} //языковое средство.Примеры интерфейсовC#Java// стандартный интерфейс C#interface IEnumerable { //стандартный интерфейс C#IEnumerator GetEnumerator();}// стандартный интерфейс Javainterface Iterable{Iterator iterator();}interface Iterator {61interface IEnumerator {object Current { get; }bool MoveNext();void Reset() ;}// реализация интерфейсаclass MyCollection : IEnumerable {public IEnumeratorGetEnumerator(){ /* реализация метода */ }}boolean hasNext();Object next();void remove();}// реализация интерфейсаclass MyCollection implementsIterable {public Iterator iterator (){ /* реализация метода */ }}Java class X extends Base implements I1, I2, …, IN {...}C#class X: Base, I1, I2, …, IN {}[!]Base - класс, от которого производится наследование, можетбыть только один!I1, I2, ...
, IN - интерфейсы, может быть несколько.Конфликты имен:Java Необходимо реализовывать только один метод, public реализация должнабыть ровно одной. (и неизвестно откуда он именно, адрес в VTBL будетсовпадать).C#понятие “явная реализация интерфейса”class X: Base, I1, I2 {void I1.f(){...}void I2.f(){...}}Явная и неявная реализация интерфейса в C#Реализация интерфейса может быть как явной, так и неявной.interface ISum{ … void foo(); }Неявная реализация интерфейсаЯвная реализация интерфейсаclass D: ISum {public void foo(){...}}class D: ISum {void ISum::foo(){...}//^ public не пишется}D d = new D();d.foo();D d = new D();d.foo(); // так нельзя, ошибка.((ISum) d).foo(); // так правильноСам Microsoft в .Net часто используют явную реализацию.62class P:I1,I2 {void I1::foo() {...} // обязательно всегда явно реализоватьvoid I2::foo() {...} // если у нас есть конфликт.//возможна умолчательная (default) реализацияpublic void foo() {...
I2::foo();}}Сравнение явной и неявной реализации интерфейсовВидимостьНеявная имплементациявсегда являелся открытой(public), поэтому к методам исвойствам можно обращатьсянапрямую.Явная имплементация всегдазакрыта (private).Чтобы получить доступ кимплементации необходимокастовать инстанцию класса кинтерфейсу (upcast to interface).ПолиморфизмНеявная имплементацияинтерфейса может бытьвиртуальной (virtual), чтопозволяет переписывать этуимплементацию в классахпотомках.Явная имплементация всегдастатична.
Она не может бытьпереписана (override) или перекрыта(new) в классах-потомках.Абстрактный класси реализацияНеявная реализация можетбыть абстрактной иреализовываться только вклассе-потомке.Явная реализация не может бытьабстрактной, но сам класс можетиметь другие абстрактные методы исам быть абстрактным.Множественное наследование-конфликты именреализацияЧистое множественное наследование - только в C++Но в других множественное наследование - по интерфейсам.Проблема множественного наследования:63X YWXYWffthis1this2W::f(){X::f(); // this1Y::f(); // this2}fнеобходимо VTBL делать из 2х частей, где во второй части хранить эту Δ, которую надо прибавлятьк this1.Нас обязывают разобраться с конфликтом имен, в отличие от Java.PythonВ любой момент времени есть как минимум 3 области видимости.1) глобальная2) module3) defКласс - еще одна область видимостиclass C(Base):i = 0def foo(self, a, b):self.i = 1def __init__(self):self.i = 0O = C()O.foo(1,2) //пошли в C, потом в Baseclass C(Base1, Base2, ...):...//пошли в C, потом вглубь в Base1, потом вBase2 и т.
д.Нет конфликта имен, но есть накладные расходы на поиск методов (как и в любом модуле).Ада83 - чисто статический язык, четкая типизация, без объектов.Ада95 - добавлены объекты.package Sample istype Base is tagged record;…end recordtype Derived is new Base with tagged record64новые членыend record.Есть обычные записи, а наследоваться могут только tagged записи.procedure P(S:Base);procedure P(D:Derived);чистое перекрытиеНо есть инкапсуляция:package File_System istype File is tagged private;procedure View(F : File);type Ada_File is new File with private;procedure View(F : Ada_File);privatetype File is taggedrecordName : String(1..20);end record;type Ada_File is new File withrecordCompiled : Boolean := False;end record;end File_System;Также в Ada95 ввели child package (наследование пакетов)package Sample.DerivedSample //подгружаем весь Sample сверхуtype Derived is new Base with tagged recordprocedure P(D:Derived)...// можем использовать все из тела BaseТеряем полную инкапсуляцию → любой может так присоединиться к любому пакету и любой класснаследовать.Мультиметод - метод, который привязан динамически к нескольким объектам.class Figure {public: virtual double getArea();}Figure *pF = …;pF->getArea();Figure* Intersection(Figure* pf1, Figure* pf2);// в общем, надо описать для любой пары производных классовCircle C;Line L;65Figure *pp = Intersection(&C, &L);// если для данных аргументов есть перегрузка, то выолнится то, что необходимо.Figure *p1 = &C, *p2 = &L;Intersection(p1, p2); // вызовет именно от двух Figure*.2009г.
- в Python появились мультиметоды.def f(a, b): # привязки к типам нет…from multimethods import *@multimethod(int, int)def foo(a, b):…@multimethod(int, double)def foo(a, b):…вызов foo(a, b) будет адресован к тому multimethod, в котором типы аргументов будут совпадать.foo(2.0, 2.0) → ошибкаТеперь есть специальный модуль multimethods {есть этот декоратор}.JavaScriptO = {}<---> O = new Object()MyClass(...) {//⇐ это функцияthis.x = 0;this.y = 0;// this имеет ссылку на этот объект.// x и y - приобретенные свойства, заводятся тут.}O = new MyClass() // вызов функции-конструктораO.prototype // есть такое свойство, имеет значение прототипа функции-конструктора.Figure() {this.x = 0;this.y = 0;this.prototype.area = function(){ return this.x * this.y; }}O = new Figure();O.x = 10;66O.y = 10;O.area();Наследование:Как-то определить прототип Figure как функции (но до первого вызова) ⇒ имеемнаследование.
Например, так:function Circle(){this.radius = 1;}Circle.prototype = Object.create(Figure.prototype);Circle.prototype.area = function() {return 3.14 * this.radius * this.radius;};Костя ([982-990], завершено)Параметрический полиморфизмОтличие параметрического полиморфизма от статического и динамического состоит в том, что присвязывании может порождаться новая сущность — класс или функция (метод). Методы реализациипараметрического полиморфизма достаточно сильно различаются для разных языков.При параметрическом полиморфизме полиморфными сущностями являются параметризованныетипы (классы) и подпрограммы (методы).Два основных понятия параметрического полиморфизма — это объявление параметризованнойабстракции (шаблона или обобщения) и конкретизация этой абстракции (т.
е. ее использование).Связывание конкретизации и объявления всегда происходит статически (во время трансляции).●●●В C++ при конкретизации шаблона происходит создание новых непараметризованныхсущностей — классов и функций.В C# при конкретизации обобщения проверяется корректность (соответствие типов).Генерация машинного кода для создаваемых непараметризованных сущностей происходитпри запуске сборки (JIT-компилятором среды .NET) на основе промежуточного кода,созданного при трансляции объявления обобщения, и информации о типах — аргументахконкретизации.В языке Java при конкретизации обобщения только проверяется корректность конкретизации(соответствие типов).
Можно рассматривать конкретизацию как процесс создания новыхнепараметризованных сущностей (классов и методов), однако вся информация об этихсущностях существует только во время трансляции и стирается при генерации объектногокода для JVM.Механизмы параметрического полиморфизма во всех языка позволяют обеспечить:● повышение надежности создаваемых абстракций за счет статического контроля соответствиятипов и повысить эффективность● уменьшить объем разрабатываемого кода (меньше усилий)generic: Ada83 - “родовые”67generic: C# (2003), Java (2005)template: C++ (90ые)Шаблоны в C++. Явная и частичная специализация шаблоновОбщий синтаксис:template <список_параметров> объявление_функции_или_классаШаблонная функцияШаблонный классtemplate<class T>void g(T x, T y){// ...}....int a, b = 0;long c = 1;g(a, b);g<int, int>(a, b);g(a, c);template<typename T>class Foo {public:Foo();void someMethod(T x);private:T x;};template <typename Т, int size> Тoperator * (VectorCT,size>&vl,Vector<T,size>&v2){T sum = 0;for (int i=0; i < size; i++)sum+=vl.getAt(i)*v2.getAt(i);return sum;}template<typename T>Foo<T>::Foo(){// ...}template<typename T>void Foo<T>::someMethod(T x){// ...}Ключевые слова typename и class при описании прототипа шаблонной функции не различаются.А вот в этом случае внутри класса при typedef нужно использовать только typename.template<typename param_t>class Foo{typedef typename param_t::baz sub_t;};Конкретизация шаблона приводит к порождению нового типа.