Полиморфизм. Абстрактные классы. Интерфейсы
Выбери формат для чтения
Загружаем конспект в формате doc
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Полиморфизм. Абстрактные классы. Интерфейсы.
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
}
}
}