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

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

  • 👀 397 просмотров
  • 📌 321 загрузка
Выбери формат для чтения
Загружаем конспект в формате doc
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Конспект лекции по дисциплине «Полиморфизм. Абстрактные классы. Интерфейсы» doc
Полиморфизм. Абстрактные классы. Интерфейсы. 1) Виртуальные методы на C++, Java, C#. Полиморфизм. Полиморфизм – разнообразие форм биологических существ. Нечто одинаковое по форме, но совершенно разные по поведению. Имеем базовый класс 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; } 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; }; 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[]) { 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(); .... } 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 { 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[] ) { 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 } } На C# виртуальные методы такие же как на C++ Record.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Record { protected int min, sec; public Record(int m, int s) { min = m; sec = s; } public int Numbersec() { return 60 * min + sec; } public bool qualification() { int k; k = Numbersec(); if (k <= 127) return true; else return false; } } } Sprint.cs class Sprint:Record { private int dec; public Sprint(int m, int s, int d):base(m,s) { dec = d; } public int Numbersec() { int k; k = 60 * min + sec; if (dec >= 5) k++; return k; } } Program.cs class Program { static void Main(string[] args) { Record a = new Record(2, 7); Sprint b = new Sprint(2, 7, 8); bool f1, f2; f1 = a.qualification(); f2 = b.qualification(); } } f1=true f2=true - qualification() -> Numbersec() из базового класса Numbersec() - виртуальная функция Record.cs using System; using System.Collections.Generic; ...... virtual public int Numbersec() { ....... Sprint.cs class Sprint:Record { .......... public override int Numbersec() // override - перегружена { ......... Program.cs class Program { static void Main(string[] args) { Record a = new Record(2, 7); Sprint b = new Sprint(2, 7, 8); bool f1, f2; f1 = a.qualification(); f2 =b.qualification(); } } f2=false ! Объект b производного класса [ b.qualification();] определяет вызов Numbersec() из производного класса Sprint 2) Вызов виртуальной функции на С++ и C# после присваивания указателей. Пусть 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 вызов из производного класса На C# полностью аналогично. Numbersec() - не виртуальная static void Main(string[] args) { Record b = new Record(2,7); Sprint 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:  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() // перегружена { return -1 ; }  int F3() // собственная { return 3 ; } }; Создаются две виртуальные таблицы Virtual Table. Указатель *__vptr, созданный в Base наследуется. Классы: Base:   *__vptr; -----------> Base Virtual Table     virtual int F1()[1] {return 1;}; [1] <------------- F1()     virtual int F2()[2] {return 2;}; [2] <------------ F2() Derive:public Base   *__vptr; наследуется -----------> Derive Virtual Table     virtual int F1()[3] {return -1;}; // перегружена [3] <----------- F1() [2] <---------- F2() int F3(){ return 3;} В каждой таблице каждая виртуальная функция указывает на ближайший экземпляр, если есть в самом классе, то на нее, нет, в базовом для класса, если нет, в базовом для базового и т.д. На верхнем уровне она обязательно есть. Эта функция и вызывается при вызове виртуальной функции. В таблице виртуальных функций базового класса обе виртуальные функции указывают на экземпляры базового класса [1] [2]. В таблице виртуальных функций производного класса F1() указывает на собственную [3] F2() на функцию базового класса (ближайшую) [2]. Вызовы: int main() { int k,m,n;   Derive *d; 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() { 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; -----------> A Virtual Table     virtual int F()[1] {return 1;}; [1] <------------- F() B:public A   *__vptr; наследуется -----------> B Virtual Table     virtual int F()[2] {return -1;}; // перегружена [2] <----------- F() C:public B   *__vptr; наследуется -----------> C Virtual Table      ближайшая в B [12] <----------- F() 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#. Чисто виртуальные функцииC++, абстрактные функции на Java, C# 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; // ошибка нельзя создать объект абстрактного класса .... Чисто виртуальная функция переопределяется в производных классах для которых создаются объекты. Пример. Имеются три класса фигур – круг, квадрат, равносторонний треугольник. У всех классов есть общее – координаты центра, длина (радиус для круга, длина стороны для прямоугольника и треугольника). Площадь для всех классов вычисляется по разному. Объявляем общим абстрактным классом геометрическую фигуру, а круг, квадрат и треугольник производными классами. #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() // перегрузка чисто виртуальной функции { 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; } 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 // производный не абстрактный класс { 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); нельзя создавать объекты абстрактных классов } } C# На C# имеются виртуальные функции как на C++ (virtual) и абстрактные (abstract) как на Java. using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { 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 : Figure // производный не абстрактный класс { public Circle(double xx,double yy,double ll):base(xx,yy,ll) { } public override double S() // перегрузка абстрактной функции { return 3.1415926*L*L; } }; class Square : Figure // производный не абстрактный класс { public Square(double xx, double yy, double ll): base(xx, yy, ll) { } public override double S() // перегрузка абстрактной функции { return L * L; } }; class Triangle : Figure // производный не абстрактный класс { public Triangle(double xx, double yy, double ll): base(xx, yy, ll) { } public override double S() // перегрузка абстрактной функции { return L * L * Math.Sqrt(3.0) / 8; } }; class Program { 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 { 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; // чисто виртуальная функция - результат в сек. }; 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: 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 и C#. Интерфейс представляет собой аналог абстрактного класса, в котором есть только абстрактные методы. Класс реализует (implements) интерфейс, если он перегружает абстрактные методы интерфейса. Java package lab7_2; interface Calculate // интерфейс доступен только для классов пакета lab7_2 // public interface Calculate доступ для любых классов { int Numbersec(); // абстрактный метод для классов, реализующих интерфейс } 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 } } C# using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { interface Calculate { int Numbersec(); // абстрактный метод для классов, реализующих интерфейс } class Record:Calculate // реализация интерфейса Calculate { protected int min,sec; public int Numbersec() // реализованный метод в интерфейсе { return min*60+sec; } public Record(int m,int s) { min=m; sec=s; } } class Sprint:Record,Calculate // производный от Record и реализующий интерфейс { public int dec; public int Numbersec() // метод интерфейса { if (dec >= 5) return 60 * min + sec + 1; else return 60 * min + sec; } public Sprint(int m, int s, int d) : base(m, s) { dec = d; } } class Program { static void Main(string[] args) { Record a = new Record(3,5); Sprint b = new Sprint(3,5,9); int m, k; m = a.Numbersec(); // вызов реализованного метода интерфейса k = b.Numbersec(); // вызов реализованного метода интерфейса Console.WriteLine("{0} {1}", m, k); Console.ReadKey(); } } } Абстрактный класс vs интерфейс на Java и C# В абстрактном классе могут быть кроме абстрактных функции, обычные. abstract class Runner { .... string Getfam() { return Fam; } } В интерфейсах могут быть только заголовки методов. Наследоваться может только один класс, а реализовываться несколько интерфейсов(Аналог множественного наследования). Java class Sprint extends Record // наследование implements Calculate, Calculate2 // реализует интерфейсы Calculate, Calculate2 { ................ C# class Sprint:Record,Calculate, Calculate2 // производный от Record и реализующий интерфейсы Calculate, Calculate2 { ............... 6) Клонирование объектов на Java и C#. Встроенный интерфейс клонирования. Мелкое и глубокое копирование на Java и C# Клонирование объекта - копирование полей объекта в другой объект. На C++ проблема присваивания динамических объектов решена перегрузкой оператора присваивания с глубоким копированием. При присваивании на java и c# объектов копируется ссылка. Java class Record { protected int min,sec; public Record(int m,int s) { min=m; sec=s; } 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 ссылаются на одну и ту же область } C# using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Record { protected int min,sec; public Record(int m, int s) { min = m; sec = s; } public void Putmin(int m) { min = m; } } class Program { static void Main(string[] args) { Record a = new Record(3, 5); Record b = new Record(4, 6); a = b; // копируется ссылка b.Putmin(7); // a.min=7 } } } На C# возможно копирование по полям с использованием struct. 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(); } 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 } } C# class Record:ICloneable // интерфейс клонирования { protected int min,sec; public Record(int m, int s) { min = m; sec = s; } public void Putmin(int m) { min = m; } public object Clone() // перегрузка метода клонирования { return new Record(min, sec); } } class Program { static void Main(string[] args) { Record a = new Record(3, 5); Record b = new Record(4, 6); a = (Record)b.Clone(); // вместо a=b b.Putmin(7); // 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); } 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; } 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(); b.Fam="Сидоров"; b.rd.min=8; // a.rd.min=6 прежнее значение } } В реализованном методе происходит глубокое (deep) клонирование. C# Мелкое клонирование. using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { 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 : ICloneable // интерфейс клонирования { public string Fam; public Record rd; // public Sportsman(string f, int m, int s) public Sportsman(string f, Record r) { Fam = f; // rd = new Record(m, s); rd = r; } public object Clone() // перегрузка клонирования { return new Sportsman(Fam, rd); } } class Program { static void Main(string[] args) { Record r1 = new Record(3, 5); Sportsman a = new Sportsman("Иванов",r1); Record r2 = new Record(4, 6); Sportsman b = new Sportsman("Петров", r2); a = (Sportsman)b.Clone(); b.Fam = "Сидоров"; // a.Fam="Петров" b.rd.Putmin(9); // a.rd.min=9! rd – копируется ссылка } } } Глубокое клонирование. using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Record :ICloneable // интерфейс клонирования { protected int min,sec; public Record(int m, int s) { min = m; sec = s; } public void Putmin(int m) { min = m; } public int Getmin() { return min; } public int Getsec() { return sec; } public object Clone() // перегрузка метода клонирования { return new Record(min, sec); } } class Sportsman : ICloneable // интерфейс клонирования { public string Fam; public Record rd; public Sportsman(string f, int m,int s) { Fam = f; rd = new Record(m,s); } public object Clone() // перегрузка клонирования { Sportsman c = new Sportsman(Fam, rd.Getmin(),rd.Getsec()); rd = (Record)rd.Clone(); // клонирование Record return c; } } class Program { static void Main(string[] args) { Sportsman a = new Sportsman("Иванов",3,5); Sportsman b = new Sportsman("Петров", 4,6); a = (Sportsman)b.Clone(); b.Fam = "Сидоров"; // a.Fam Петров b.rd.Putmin(9); // a.rd.min=4 } } }
«Полиморфизм. Абстрактные классы. Интерфейсы» 👇
Готовые курсовые работы и рефераты
Купить от 250 ₽
Решение задач от ИИ за 2 минуты
Решить задачу
Помощь с рефератом от нейросети
Написать ИИ
Получи помощь с рефератом от ИИ-шки
ИИ ответит за 2 минуты

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

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

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

Перейти в Telegram Bot