Справочник от Автор24
Поделись лекцией за скидку на Автор24

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

  • 👀 419 просмотров
  • 📌 393 загрузки
Выбери формат для чтения
Загружаем конспект в формате pdf
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Конспект лекции по дисциплине «Полиморфизм. Абстрактные классы. Интерфейсы» pdf
Полиморфизм. Абстрактные классы. Интерфейсы. 1) Виртуальные методы на C++, Java. Полиморфизм. Полиморфизм – разнообразие форм биологических существ. Нечто одинаковое по форме, но совершенно разные по поведению. Имеем базовый класс Record и производный Sprint с полем dec. Выполним перегрузку метода Numbersec. Numbersec объявим виртуальнам методом. #include "stdafx.h" class Record { public: Record(int m,int s); // конструктор с параметрами virtual int Numbersec(); // виртуальный метод protected: int min,sec; }; Record::Record(int m,int s) { min=m; sec=s; } int Record::Numbersec() { int k; k=60*min+sec; return k; } class Sprint : public Record { public: Sprint(int m,int s,int d); int Numbersec(); // перегруженный виртуальный метод // в производном классе ост. виртуальным private: int dec; }; Sprint::Sprint(int m,int s,int d):Record(m,s) { dec=d; 1 } int Sprint::Numbersec() // перегрузка с округлением { int k; k=60*min+sec; if(dec>=5) k++; return k; } int _tmain(int argc, _TCHAR* argv[]) { Record a(2,7); Sprint b(2,7,8); int m,k; // объект вызывает собственный метод класса m=a.Numbersec(); // m=127 k=b.Numbersec (); // m=128 return 0; } Убрав virtual в программе получим такой же результат, внутренняя структура заголовка виртуальных и не виртуальных функций различна. Отличия в работе виртуальных функций в программах Добавим не виртуальный метод в Record: bool qualification, метод возвращает true (разряд выполнен), если результат меньше или равен 2 мин. 7 сек. Метод qualification класса Record, наследуется без перегрузки в Sprint. #include "stdafx.h" class Record { public: Record(int m,int s); int Numbersec(); bool qualification(); protected: int min,sec; }; 2 Record::Record(int m,int s) { min=m; sec=s; } int Record::Numbersec() { int k; k=60*min+sec; return k; } bool Record::qualification() { int k; k=Numbersec(); // результат в секундах if(k<=127) return true; // выполнил else return false; // не выполнил } class Sprint : public Record { public: Sprint(int m,int s,int d); int Numbersec(); // перегруженный метод private: int dec; }; Sprint::Sprint(int m,int s,int d):Record(m,s) { dec=d; } int Sprint::Numbersec() { int k; k=60*min+sec; if(dec>=5) k++; return k; } int _tmain(int argc, _TCHAR* argv[]) { 3 Record a(2,7); Sprint b(2,7,8); int m,k; bool f1,f2; f1=a.qualification(); // f1=true f2=b.qualification(); // f2=true ! qualification вызвал метод Numbersec из базового класса return 0; } Если Numbersec объявить виртуальным { public: Record(int m,int s); virtual int Numbersec(); bool qualification(); protected: int min,sec; }; ...... bool f1,f2; f1=a.qualification(); // f1=true f2=b.qualification(); // f2=false !! qualification вызвал метод Numbersec из произв. класса Принцип вызова: class One // базовый класс { ...... void F1(); void F2(); void F3(); virtual int Fv(); // виртуальная функция перегружаемая ....... }; void One::F1() { ..... F2(); .... } void One::F2() { ..... F3(); .... 4 } void One::F3() { ..... Fv(); // вызов виртуальной функции .... } int One::Fv() // виртуальная функция в базовом классе { return 1; } class Two: public One // производный класс { .... int Fv(); // перегрузка виртуальной функции в производном классе } int Two::Fv() // виртуальная функция в производном классе { return 2; } Функции F1(), F2(), F3() не перегружаются в Two, а наследуются int main(int argc, char* argv[]) { Two b; b.F1(); // вызов наследуемой функции } Цепочки вызовов: b.F1() -> F2() -> F3() -> Fv() –виртуальная, вернет 2 (b. объект Two) не виртуальная Fv() вернет 1 для b.F1(). Вызов виртуальной функции определяется классом вызывающего объекта. b.F1(); b - объект класса Two Не виртуальной функции - классом функции, которая вызывает виртуальную. b.F1(); --> F2() -> F3() --> Fv(); F3() - функция класса One На Java все функции виртуальные! Ключевое слово virtual отсутствует class Record { 5 protected int min,sec; public Record(int m,int s) { min=m; sec=s; } public int Numbersec() { return 60*min+sec; } public boolean qualification() { int k; k=Numbersec(); if(k<=127) return true; else return false; } } class Sprint extends Record { private int dec; Sprint(int m,int s,int d) { super.(m,s); dec=d; } public int Numbersec() { int k; k=60*min+sec; if(dec>=5) k++; return k; } } public class lab7 { public static void main (String args[] ) { 6 Record a; a=new Record(2,7); Sprint b; b=new Sprint(2,7,8); boolean f1,f2; f1=a.qualification(); // f1=true f2=b.qualification(); // f2=false } } 2) Вызов виртуальной функции на С++ после присваивания указателей. Пусть Numbersec не виртуальная функция, перегружена в Sprint с округлением. вызов метода динамическим объектом: int k; Record *b; // указатели на базовый класс Sprint *d; // указатель на производный класс b=new Record(2,7); d=new Sprint(2,7,8); b=d; // присваивание указателю базового класса указателя производного k=b->Numbersec(); // k=127 вызов из базового класса Если Numbersec() виртуальная, k=b->Numbersec(); // k=128 вызов из производного класса 3) Таблицы виртуальных функций для полиморфных классов. Принципы вызова виртуальных фукций. Имеется базовый класс Base и производный Derive с перегруженной функцией F1() и собственной F3(). class Base { public: int F1() // не виртуальная { return 1; } }; class Derive: public Base { public: 7 int F1() // перегружена, не виртуальная { return -1; } int F3() // собственная производного { return 3; } }; int main() { int k; Derive *d; Base *b; d=new Derive; b=d; // присваиваются только поля и методы базового класса k=b ->F1();// в b содержится только F1 из базового, которая вызывается k=1 // k=b->F3(); // этой функции в базовом нет } Пусть F1 виртуальная. Добавим в базовый виртуальную F2(), которая в производном не перегружается, но наследуется. Если есть хотя бы одна виртуальная функция, класс называется полиморфным, при компиляции в базовом классе создается скрытый указатель, *__vptr, который наследуется и в каждом классе создается по таблице виртуальных функций. class Base { public: FunctionPointer *__vptr; // автоматически virtual int F1() // первая виртуальная функция перегружается { return 1; } virtual int F2() // вторая виртуальная функция задана в базовом классе в производном не перегружается { return 2; } }; class Derive: public Base { public: int F1() // перегружена 8 { return -1 ; } int F3() // собственная { return 3 ; } }; Создаются две виртуальные таблицы Virtual Table. Указатель *__vptr, созданный в Base наследуется. Классы: Base: *__vptr; -----------> virtual int F1()[1] {return 1;}; [1] <------------virtual int F2()[2] {return 2;}; [2] <-----------Derive:public Base *__vptr; наследуется -----------> virtual int F1()[3] {return -1;}; // перегружена Base Virtual Table F1() F2() Derive Virtual Table [3] <----------F1() [2] <---------F2() int F3(){ return 3;} В каждой таблице каждая виртуальная функция указывает на ближайший экземпляр, если есть в самом классе, то на нее, нет, в базовом для класса, если нет, в базовом для базового и т.д. На верхнем уровне она обязательно есть. Эта функция и вызывается при вызове виртуальной функции. В таблице виртуальных функций базового класса обе виртуальные функции указывают на экземпляры базового класса [1] [2]. В таблице виртуальных функций производного класса F1() указывает на собственную [3] F2() на функцию базового класса (ближайшую) [2]. Вызовы: int main() { int k,m,n; Derive *d; 9 Base *b; d=new Derive; b=d; // скрытый указатель копируется в b, становится указателем производного класса ! k=b ->F1();// k=-1 F1 производного класса из виртуальной таблицы производного m=b->F2(); // m=2 из базового, ближайшая по виртуальной таблице производного // n=b->F3(); // ошибка, F3 в базовом нет } При присваивании b=d; указатель *__vptr устанавливается на виртуальную таблицу d! k=b ->F1(); По таблице виртуальных функций для производного класса ближайшая F1 – собственная. k=-1. m=b->F2(); По таблице виртуальных функций для производного класса ближайшая F2 – из базового. m=2 // n=b->F3(); F3 в базовом нет b не может вызвать. В базовом наследуемая не виртуальная функция F(), которая вызывает перегружаемую виртуальную функцию F1(). class Base { public: FunctionPointer *__vptr; virtual int F1() // виртуальная функция перегружается { return 1; } int F() // не виртуальная функция наследуется { int k; k=F1(); // вызов виртуальной функции return k+1; } }; class Derive: public Base { public: int F1() // перегруженная виртуальная { return -1 ; } }; int main() 10 { int k; Derive d; k=d.F(); // k=0 вызывается F1 из производного. Вызывает объект d – указатель *__vptr устанавливается на виртуальную таблицу Derive. Далее в цепочке вызовов через F() вызывается виртуальная функция F1(). При вызове из таблицы для Derive находится нужный экземпляр – собственная F1(). class A { public: virtual int F() {return 1;} }; class B:public A { public: int F() { return -1;} }; class C:public B { private: int unused; }; int _tmain(int argc, _TCHAR* argv[]) { C с; int k; k=с.F(); // k=-1 функция из B ближайшая return 0; } A: *__vptr; virtual int F()[1] -----------> {return 1;}; [1] <------------- B:public A *__vptr; наследуется -----------> virtual int F()[2] {return -1;}; // перегружена C:public B *__vptr; наследуется ближайшая в B A Virtual Table F() B Virtual Table [2] <----------F() -----------> [12] <----------- C Virtual Table F() 11 int F() {return 1;} // невиртуальная int _tmain(int argc, _TCHAR* argv[]) { C x; int k; k=с.F(); // k=-1 функция из B return 0; } Вызовется метод из B, т.к. B базовый для C по наследованию. 4) Абстрактные классы на C++, Java. Чисто виртуальные функции C++, абстрактные функции на Java C++ Абстрактный класс на C++ – класс содержащий по крайней мере одну чисто виртуальную функцию. class Pimer { ..... virtual int Fun(int c)=0; // чисто виртуальная функция, тело функции равно нулю .... } Пример пустой виртуальной функции (не чисто виртуальной) class Pimer // не абстрактный класс { ..... virtual int Fun(int c); .... } .... int Primer::Fun(int c) // функция виртуальная, но не чисто виртуальная ! { } Если класс абстрактный, то нельзя создавать объектов данного класса. int main(int argc, char* argv[]) { Primer a; // ошибка нельзя создать объект абстрактного класса .... 12 Чисто виртуальная функция переопределяется в производных классах для которых создаются объекты. Пример. Имеются три класса фигур – круг, квадрат, равносторонний треугольник. У всех классов есть общее – координаты центра, длина (радиус для круга, длина стороны для прямоугольника и треугольника). Площадь для всех классов вычисляется по разному. Объявляем общим абстрактным классом геометрическую фигуру, а круг, квадрат и треугольник производными классами. #include class Figure // абстрактный класс { protected: double X,Y,L; // координаты центра и длина public: double GetLen(); Figure(double xx,double yy,double ll); // конструктор virtual double S()=0; // чисто виртуальная функция }; Figure::Figure(double xx,double yy,double ll) { X=xx; Y=yy; L=ll; } double Figure:: GetLen() { return L; } class Circle:public Figure // производный не абстрактный класс { public: Circle(double xx,double yy,double ll); // конструктор не наследуется double S(); }; Circle::Circle(double xx,double yy,double ll):Figure(xx,yy,ll) // вызов базового { } double Circle::S() // перегрузка чисто виртуальной функции { 13 return 3.1415926*L*L; } class Square:public Figure // производный неабстрактный класс { public: Square(double xx,double yy,double ll); double S(); }; Square::Square(double xx,double yy,double ll):Figure(xx,yy,ll) { } double Square::S() // перегрузка чисто виртуальной функции { return L*L; } class Triangle:public Figure // производный неабстрактный класс { public: Triangle(double xx,double yy,double ll); double S(); }; Triangle::Triangle(double xx,double yy,double ll):Figure(xx,yy,ll) { } double Triangle::S() // перегрузка чисто виртуальной функции { return L*L*sqrt(3.0)/8; } int _tmain(int argc, _TCHAR* argv[]) { double yc,ys,yt; Circle c(3,4,5); yc=c.S(); Square s(3,4,5); ys=s.S(); Triangle t(3,4,5); yt=t.S(); // Figure f(3,4,5); нельзя создавать объекты абстрактных классов return 0; } 14 Java На Java чисто виртуальные функции называются абстрактными. Абстрактный класс на Java – класс содержащий по крайней мере одну абстрактную функцию. package lab7_1; abstract class Figure // абстрактный класс { protected double X,Y,L; // координаты центра и длина public double GetLen() { return L; } public Figure(double xx,double yy,double ll) // конструктор { X=xx; Y=yy; L=ll; } public abstract double S(); // абстрактная функция } class Circle extends Figure // производный не абстрактный класс { public Circle(double xx,double yy,double ll) { super(xx,yy,ll); // вызов базового конструктора } public double S() // перегрузка абстрактной функции { return 3.1415926*L*L; } }; class Square extends Figure // производный не абстрактный класс { public Square(double xx,double yy,double ll) { super(xx,yy,ll); // вызов базового конструктора } public double S() // перегрузка абстрактной функции { return L*L; } }; class Triangle extends Figure // производный не абстрактный класс 15 { public Triangle(double xx,double yy,double ll) { super(xx,yy,ll); // вызов базового конструктора } public double S()// перегрузка абстрактной функции { return L*L*Math.sqrt(3.0)/8; } }; public class lab7 { public static void main (String args[] ) { double yc,ys,yt; Circle c=new Circle(3,4,5); yc=c.S(); Square s=new Square(3,4,5); ys=s.S(); Triangle t=new Triangle(3,4,5); yt=t.S(); // Figure f=new Figure(3,4,5); нельзя создавать объекты абстрактных классов } } Пусть имеется класс Record для фиксации результатов по бегу на длинные дистанции (min, sec) и производный класс Sprint с десятыми долями секунды (min, sec, dec) для результатов по бегу на короткие дистанции. В модели соревнований возможны три типа спортсменов: бегун на короткие дистанции (Sprinter), бегун на длинные дистанции (Stayer), спортсмен – двоеборец (Combine). У всех есть фамилия (string Fam). У Sprinter - 1 результат класса Sprint , У Stayer – 1 результат класса Record, У Combine – 1 результат типа Sprint и 1 результат типа Record. Создается абстрактный базовый класс Runner (бегун), а Sprinter, Stayer и Combine производные. #include "stdafx.h" #include class Record 16 { protected: int min,sec; public: Record(int m,int s); int Numbersec(); }; Record::Record(int m,int s) { min=m; sec=s; } int Record::Numbersec() { return 60*min+sec; } class Sprint : public Record { public: Sprint(int m,int s,int d); int Numbersec(); private: int dec; }; Sprint::Sprint(int m,int s,int d):Record(m,s) { dec=d; } int Sprint::Numbersec() // перегрузка метода { int k; k=60*min+sec; if(dec>=5) k++; return k; } class Runner // абстрактный класс для спортсменов { protected: char Fam[30]; public: Runner(char *f); // конструктор с параметрами virtual int Totalsec()=0; // чисто виртуальная функция - результат в сек. 17 }; Runner::Runner(char *f) // конструктор - определена только фамилия { strcpy(Fam,f); } class Stayer:public Runner // один результат Record - длинная дистанция { protected: Record Rezult; // 1 бег на длинную дистанцию public: Stayer(char *f,int m,int s); // конструктор int Totalsec(); // перегрузка чисто виртуальной функции }; Stayer::Stayer(char *f,int m,int s):Runner(f) // вызов базового конструктора { Rezult=Record(m,s); } int Stayer::Totalsec() { return Rezult.Numbersec(); // 1 результат бега на длинную дистанцию } class Sprinter:public Runner // один результат Sprint { protected: Sprint Rezult; // 1 бег на короткую дистанцию public: Sprinter(char *f,int m,int s,int d); int Totalsec(); // перегрузка чисто виртуальной функции }; Sprinter::Sprinter(char *f,int m,int s,int d):Runner(f) { Rezult=Sprint(m,s,d); } int Sprinter::Totalsec() { return Rezult.Numbersec(); // 1 результат бег на короткую дистанцию } class Combine:public Runner // 1 результат Record, 1 - Sprint { protected: 18 Record Rezult1; // 1 бег на длинную дистанцию Sprint Rezult2; // 1 бег на короткую дистанцию public: Combine(char *f,int m1,int s1,int m2,int s2,int d); int Totalsec(); // перегрузка чисто виртуальной функции }; Combine::Combine(char *f,int m1,int s1,int m2,int s2,int d):Runner(f) { Rezult1=Record(m1,s1); Rezult2=Sprint(m2,s2,d); } int Combine::Totalsec() { return Rezult1.Numbersec()+Rezult2.Numbersec(); // сумма бега на короткую и длинную дистанции } int _tmain(int argc, _TCHAR* argv[]) { int m,n,k; // Runner a; нельзя создать объект абстрактного класса Stayer b("Иванов",2,7); Sprinter c("Петров",2,7,8); Combine d("Сидоров",2,7,2,7,8); m=b.Totalsec(); // m=127 n=c.Totalsec(); // n=128 с округлением k=d.Totalsec(); // k=127+128=255 return 0; } 5) Интерфейсы на Java. Интерфейс представляет собой аналог абстрактного класса, в котором есть только абстрактные методы. Класс реализует (implements) интерфейс, если он перегружает абстрактные методы интерфейса. Java package lab7_2; interface Calculate // интерфейс доступен только для классов пакета lab7_2 // public interface Calculate доступ для любых классов { int Numbersec(); // абстрактный метод для классов, реализующих интерфейс } 19 class Record implements Calculate // реализация интерфейса Calculate { protected int min,sec; public int Numbersec() // реализованный метод в интерфейсе { return min*60+sec; } public void Init(int m,int s) { min=m; sec=s; } } class Sprint extends Record implements Calculate // реализует интерфейс Calculate { private int dec; public int Numbersec() // метод с округлением по dec { if(dec>=5) return min*60+sec+1; else return min*60+sec; } public void Init(int m,int s,int d) { min=m; sec=s; dec=d; } } public class lab7 { public static void main(String[] args) { Record a=new Record(); a.Init(3, 10); Sprint b=new Sprint(); b.Init(3, 10,8); int m,k; m=a.Numbersec(); // вызов метода, реализованного в интерфейсе k=b.Numbersec(); // вызов метода, реализованного в интерфейсе System.out.printf("%d %d \n",m,k); // 190 191 } } 20 Абстрактный класс на Java В абстрактном классе могут быть, кроме абстрактных, обычные функции. abstract class Runner { .... string Getfam() { return Fam; } } В интерфейсах могут быть только заголовки методов. Наследоваться может только один класс, а реализовываться несколько интерфейсов(Аналог множественного наследования). Java class Sprint extends Record // наследование implements Calculate, Calculate2 // реализует интерфейсы Calculate, Calculate2 { ................ 6) Клонирование объектов на Java. Встроенный интерфейс клонирования. Мелкое и глубокое копирование на Java Клонирование объекта - копирование полей объекта в другой объект. На C++ проблема присваивания динамических объектов решена перегрузкой оператора присваивания с глубоким копированием. При присваивании на java объектов копируется ссылка. Java class Record { protected int min,sec; public Record(int m,int s) { min=m; sec=s; } 21 public void Putmin(int m) { min=m; } public class lab7 { public static void main(String[] args) { Record a=new Record(3,10); Record b=new Record(4,23); a=b; // копируется ссылка a.min=4 a.sec=23 b.Putmin(12); // a.min=12 т.к. a,b ссылаются на одну и ту же область } } public static void main(String[] args) { Record a=new Record(3,10); Record b=new Record(4,23); a=b; // копируется ссылка a.min=4 a.sec=23 b.Putmin(12); // a.min=12 т.к. a,b ссылаются на одну и ту же область } Java Добавим интерфейс клонирования. package lab7_6; class Record implements Cloneable // реализация встроенного интерфейса { protected int min,sec; public Record(int m,int s) { min=m; sec=s; } public void Putmin(int m) { min=m; } public Object clone() // перегрузка метода интерфейса Cloneable из суперкласса { try { return (Record)super.clone(); } 22 catch(CloneNotSupportedException e) { } return this; } } public class lab7 { public static void main(String[] args) { Record a=new Record(3,10); Record b=new Record(4,23); a=(Record)b.clone(); // a - копия b a.min=4 a.sec=23 b.Putmin(12); // a.min=4 } } В основном классе есть поля вспомогательного класса Java package lab7_6; class Record { protected int min,sec; public Record(int m,int s) { min=m; sec=s; } public void Putmin(int m) { min=m; } } class Sportsman implements Cloneable // реализация клонирования { public String Fam; public Record rd; // результат спортсмена public Sportsman(String f,int m,int s) { Fam=f; rd=new Record(m,s); 23 } public Object clone() // перегрузка интерфейса клонирования { try { Sportsman clone=(Sportsman)super.clone(); return clone; } catch(CloneNotSupportedException e) { } return this; } } public class lab7 { public static void main(String[] args) { Sportsman a=new Sportsman("Иванов",5,15); Sportsman b=new Sportsman("Петров",6,20); a=(Sportsman)b.clone(); // a.Fam="Петров" a.rd.min=6 a.rd.sec=20 b.Fam="Сидоров"; // a.Fam="Петров" OK b.rd.min=8; // a.min=8 ! копировалась ссылка rd ! } } В реализованном методе происходит мелкое (shallow) клонирование. Поле rd (типа Record) является ссылкой в Sportsman и при клонировании Sportsman копируется адрес. Оба поля указывают на одну область памяти и изменение поля в b автоматически меняет в a. Исправление. Record также делается Cloneable. package lab7_6; class Record implements Cloneable // возможность клонирования { protected int min,sec; public Record(int m,int s) { min=m; sec=s; } public void Putmin(int m) { min=m; 24 } public Object clone() // перегрузка интерфейса клонирования { try { return (Record)super.clone(); } catch(CloneNotSupportedException e) { } return this; } } class Sportsman implements Cloneable // реализация клонирования { public String Fam; public Record rd; // результат спортсмена public Sportsman(String f,int m,int s) { Fam=f; rd=new Record(m,s); } public Object clone() // перегрузка клонирования { try { Sportsman clone=(Sportsman)super.clone(); clone.rd=(Record)rd.clone(); // клонирование поля rd (Record) ! return clone; } catch(CloneNotSupportedException e) { } return this; } } public class lab7 { public static void main(String[] args) { Sportsman a=new Sportsman("Иванов",5,15); Sportsman b=new Sportsman("Петров",6,20); a=(Sportsman)b.clone(); 25 b.Fam="Сидоров"; b.rd.min=8; // a.rd.min=6 прежнее значение } } В реализованном методе происходит глубокое (deep) клонирование. 26
«Полиморфизм. Абстрактные классы. Интерфейсы» 👇
Готовые курсовые работы и рефераты
Купить от 250 ₽
Решение задач от ИИ за 2 минуты
Решить задачу
Помощь с рефератом от нейросети
Написать ИИ
Получи помощь с рефератом от ИИ-шки
ИИ ответит за 2 минуты

Тебе могут подойти лекции

Смотреть все 588 лекций
Все самое важное и интересное в Telegram

Все сервисы Справочника в твоем телефоне! Просто напиши Боту, что ты ищешь и он быстро найдет нужную статью, лекцию или пособие для тебя!

Перейти в Telegram Bot