Выбери формат для чтения
Загружаем конспект в формате doc
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Шаблоны (templates). Обобщенное программирование(Generics) .
1) Шаблоны функций (template) на C++
Шаблоны предназначены для повторного использования кода (reuse) в различных классах.
Имеется фрагмент кода, который в проекте последовательно применяется к различным типам данных.
Пример.
Обмен значений переменных (int float, char, объекты класса) a, b
void Change(int &a,int &b)
{
c=a;
a=b;
b=c;
}
Для char можно сделать перегрузку:
void Change(char &a,char &b)
{
c=a;
a=b;
b=c;
}
Код у этих функций одинаковый!
Для общей функции обмена используется шаблон функции, при вызове компилятор меняет параметр на конкретный тип.
#include "stdafx.h"
class Record
{
private:
int min,sec;
public:
void Init(int m,int s);
};
void Record::Init(int m,int s)
{
min=m;
sec=s;
}
template // шаблон с одним параметром T
void Change(T &a,T &b) // Функция с шаблоном T
{
T c;
c=a;
a=b;
b=c;
}
int _tmain(int argc, _TCHAR* argv[])
{
int a=5,b=3;
double c=6.0,d=8.0;
Change(a,b); // вызов с целыми аргументами
Change(c,d); // вызов с вещественными аргументами
Record x;
Record y;
x.Init(3,5);
y.Init(5,3);
Change(x,y); // с аргументами объектами Record
return 0;
}
В проекте имеются 2 независимых класса Record и Angle, у которых имеются поля с одинаковыми названиями min, sec. Функция округления до минут представляет собой шаблон.
#include "stdafx.h"
class Record
{
private:
int min,sec;
public:
Record(int m,int s);
int Getmin();
int Getsec();
};
Record::Record(int m,int s)
{
min=m;
sec=s;
}
int Record::Getmin()
{
return min;
}
int Record::Getsec()
{
return sec;
}
class Angle
{
private:
int grad,min,sec;
public:
Angle(int g,int m,int s);
int Getmin();
int Getsec();
};
Angle::Angle(int g,int m,int s)
{
grad=g;
min=m;
sec=s;
}
int Angle::Getmin()
{
return min;
}
int Angle::Getsec()
{
return sec;
}
template
int Roundmin(T &a) // шаблон функции
{
if(a.Getsec()>=30)
return a.Getmin()+1;
else
return a.Getmin();
}
int _tmain(int argc, _TCHAR* argv[])
{
Record r(3,50);
Angle a(3,4,34);
int m,n;
m=Roundmin(r); // m=4
n=Roundmin(a); // n=5
return 0;
}
2) Шаблоны классов на C++
Шаблон - массив с методами ввода и вычисления суммы элементов массива. Для Record необходима перегрузка операторов.
#include "stdafx.h"
#include
using namespace std;
class Record
{
private:
int min,sec;
Record operator+(Record b); // перегрузка сложения Record+Record
};
Record operator >> (istream &o,Record &r)
{
cin >> r.min >> r.sec;
return r;
}
Record Record::operator+(Record b)
{
Record c;
c.min=this->min+b.min;
c.sec=this->sec+b.sec;
if(c.sec>=60)
{
c.min++;
c.sec -= 60;
}
return c;
}
template class Array // общий класс Array для типа данных T
{
public:
Array(int s); // конструктор с параметром
void Read(); // ввод элементов массива
T sum(); // сумма элементов массива типа T, результат тип T
private:
T *data; // массив данных
int size; // размер массива
};
template Array:: Array(int s) // конструктор
{
data=new T[s];
size=s;
}
template void Array::Read()
{
int i;
for(i=0;i> data[i]; // cin>> должна быть определена!
}
}
template T Array::sum()
{
T a;
int i;
a=data[0];
for(i=1;i Z(5); // массив целых чисел
int s;
Z.Read();
s=Z.sum();
Array Y(4); // массив вещественных чисел
double p;
Y.Read();
p=Y.sum();
Array X(3); // массив объектов Record
Record R;
X.Read();
R=X.sum(); // сумма элементов Record
return 0;
}
3) Обобщенные методы и классы (generics) на Java
Обычный метод Change в Java из-за ссылок не работает.
В С++ метод обмена Record:
public static void Change(Record &a,Record &b)
{
Record c;
c=a;
a=b;
b=c;
}
Аналог в Java:
public static void Change(Record a,Record b)
{
Record c;
c=a;
a=b;
b=c;
}
public static void main (String args[] )
{
Record s=new Record(1,2);
Record q=new Record(2,1);
Record.Change(s, q); // s: min=1 sec=2 q: min=2 sec=1 остались прежними
}
Без метода обмен в коде совершается.
public static void main (String args[] )
{
Record s=new Record(1,2);
Record q=new Record(2,1);
Record w;
w=s;
s=q;
q=w; // s: min=2 sec=1 q: min=1 sec=2
}
Вариант Change обмена по полям.
public static void Change(Record a,Record b)
{
int m,s;
m=a.min;
s=a.sec;
a.min=b.min;
a.sec=b.sec;
b.min=m;
b.sec=s;
}
Обобщенный метод обмена значениями не работает.
public class lab8
{
public static void Change(T a,T b) // - параметр метода, заменяемый конкретным типом
{
T c;
c=a; // обмен локальными ссылками
a=b;
b=c;
}
......
}
public static void main (String args[] )
{
int a=5,b=3;
Change(a,b); // те же значения
Record s=new Record(1,2);
Record q=new Record(2,1);
Change(s,q); // те же значения
}
Обобщенный метод перестановки элементов массива в обратном порядке.
public class lab8
{
public static void Reverse(T[] x) // x – массив обобщенного типа T
{
T temp;
int i;
int k;
k=x.length;
for(i=0;i<=k/2;i++)
{
temp=x[i];
x[i]=x[k-i-1];
x[k-i-1]=temp;
}
}
public static void main (String args[] )
{
Integer nums[]={1,2,3,4};
Record r=new Record[5];
Reverse(nums); // 4 3 2 1
for(i=0;i<5;i++)
{
r[i]=new Record(i,1);
}
Reverse(r); // {4 1} {3 1} {2 1} {1 1} {0 1}
}
}
Аналог варианта C++ Roundmin приводит к ошибке компиляции.
public static int Roundmin(T a)
{
if(a.Getsec()>=30) // a не содержит метода Getsec() ! a – объект класса Object
return a.Getmin()+1;
else
return a.Getmin();
}
После запуска программы информация о реальном типе, который замещает T отсутствует!
Можно использовать аргументы ограниченного типа (bounded type).
class Record
{
protected int min,sec;
public Record(int m,int s)
{
min=m;
sec=s;
}
public int Getsec()
{
return sec;
}
}
class Sprint extends Record
{
private int dec;
Sprint(int m,int s,int d)
{
super(m,s);
dec=d;
}
}
public class lab8
{
.....
public static int Roundmin(T a)
// обобщенный метод для класса Record и всех его производных, например, Sprint
{
if(a.Getsec()>=30)
return a.Getmin()+1;
else
return a.Getmin();
}
......
public static void main (String args[] )
{
Record v=new Record(2,40);
Sprint z=new Sprint(3,35,2);
int m,n;
m=Roundmin(v);
n=Roundmin(z);
}
Пример обобщенного класса массива общего типа на Java
Аналог класса обобщенного массива для C++ создать не удается. Array содержит конструктор и метод суммы элементов.
class Array // класс с параметром
{
private T[] ob;
private int size;
Array(T[]o)
{
ob=o;
}
T sum()
{
int i;
T a;
a=ob[0];
for(i=1;i Z=new Array(num); // создан массив из целых чисел
Record r=new Record[5];
for(i=0;i<5;i++)
{
r[i]=new Record(i,1);
}
Array X=new Array (r); // создан массив из объектов Record
Для массивов типа Record и производных создается обобщенный класс с ограниченным типом. В Record добавлен метод Add позволяющий складывать 2 Record и производные от него.
class Record
{
protected int min,sec;
public Record(int m,int s)
{
min=m;
sec=s;
}
public int Getmin()
{
return min;
}
public int Getsec()
{
return sec;
}
public static void Ch(Record a,Record b)
{
Record c=new Record(0,0);
c=a;
a=b;
b=c;
}
public Record Add(Record a,Record b) // добавлен метод сложения двух объектов
{
Record c=new Record(0,0);
c.min=a.min+b.min;
c.sec=a.sec+b.sec;
if(c.sec>=60)
{
c.min++;
c.sec-=60;
}
return c;
}
}
class Sprint extends Record
{
private int dec;
Sprint(int m,int s,int d)
{
super(m,s);
dec=d;
}
}
class Array // параметр T только для Record и подклассов, например, Sprint
{
private T[] ob;
private int size;
public Array(T[]o)
{
ob=o;
}
T sum()
{
int i;
T a;
a=ob[0];
for(i=1;i X=new Array (r);
Sprint u=new Sprint[4];
for(i=0;i<4;i++)
{
u[i]=new Sprint(1,i,3);
}
Array X=new Array (r);
Array Y=new Array (u); // массив объектов из Sprint
Record s=new Record(0,0);
Record t=new Record(0,0);
s=X.sum(); // s: min=10 sec=5
t=Y.sum(); // t: min=4 sec=6
}
3) Обобщенные методы и классы (generics) на C#
Пример.
Обмен значений переменных (int float, char, объекты класса) a, b
аналогично C++.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Record
{
private int min, sec;
public void Init(int m, int s)
{
min = m;
sec = s;
}
}
class Program
{
static void Change(ref T a, ref T b) // метод с параметром
{
T c;
c = a;
a = b;
b = c;
}
static void Main(string[] args)
{
int a = 5, b = 3;
double c = 6.0, d = 8.0;
Change(ref a, ref b); // вызов с целыми аргументами
Change(ref c, ref d); // вызов с вещественными аргументами
Record x=new Record();
Record y=new Record();
x.Init(3, 5);
y.Init(5, 3);
Change(ref x, ref y); // с аргументами объектами Record
}
}
}
В проекте имеются класс Record и прозводный класс Sprint. Функция округления до минут представляет собой шаблон с ограничениями на параметры.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Record
{
private int min, sec;
public Record(int m, int s)
{
min = m;
sec = s;
}
public int Getmin()
{
return min;
}
public int Getsec()
{
return sec;
}
}
class Sprint : Record
{
private int dec;
public Sprint(int m, int s, int d)
: base(m, s)
{
dec = d;
}
}
class Program
{
static int Roundmin(T a) where T:Record // параметр T тип Record или производные классы
{
if (a.Getsec() >= 30)
return a.Getmin() + 1;
else
return a.Getmin();
}
static void Main(string[] args)
{
Record r=new Record(3,40);
Sprint s = new Sprint(5, 45, 50);
int m,n;
m = Roundmin(r);
n = Roundmin(s);
}
}
}
Пример обобщенного класса массива общего типа на C#
Аналог класса обобщенного массива для C++ создать не удается. Array содержит конструктор и метод суммы элементов.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Record
{
private int min, sec;
public Record(int m, int s)
{
min = m;
sec = s;
}
public int Getmin()
{
return min;
}
public int Getsec()
{
return sec;
}
public Record Add(Record a, Record b) // добавлен метод сложения двух объектов
{
Record c = new Record(0, 0);
c.min = a.min + b.min;
c.sec = a.sec + b.sec;
if (c.sec >= 60)
{
c.min++;
c.sec -= 60;
}
return c;
}
}
class Sprint : Record
{
private int dec;
public Sprint(int m, int s, int d)
: base(m, s)
{
dec = d;
}
}
class Array where T:Record
{
private T[] ob;
private int size;
public Array(T[] o)
{
ob = o;
}
public T sum()
{
int i;
T a;
a = ob[0];
for (i = 1; i < ob.Length; i++)
{
a = (T)a.Add(a, ob[i]);
}
return a;
}
}
static void Main(string[] args)
{
Record []rec=new Record[5];
int i;
for(i=0;i<5;i++)
{
rec[i]=new Record(i,1);
}
Sprint[] sp = new Sprint[4];
for (i = 0; i < 4; i++)
{
sp[i] = new Sprint(1, i,4);
}
Array X = new Array(rec);
Array Y = new Array(sp);
Record f=new Record(0,0);
Record t=new Record(0,0);
f=X.sum(); // s: min=10 sec=5
....
4) Делегаты на C#
Делегат – объект для вызовов методов с одинаковыми аргументами и возвращаемым типом (одинаковой сигнатурой).
Пример.
В проекте калькулятор целых чисел имеется ряд вычислительных методов, у всех 2 аргумента целого типа и они возвращают целое число. Объявлен делегат для работы с указанными методами.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static int Add(int a, int b)
{
return a + b;
}
static int Sub(int a, int b)
{
return a - b;
}
static int Mult(int a, int b)
{
return a * b;
}
static int Div(int a, int b)
{
return a / b;
}
public delegate int Calc(int a, int b); // делегат Calc для вычислений
static void Main(string[] args)
{
Calc A = Add; // A экземпляр делегата для сложения
int Result;
Result = A(4, 6); // вызов метода сложения Result=10
Calc S = Sub; // B экземпляр делегата для вычитания
Result = S(6, 2); // вызов метода сложения Result=14
A = Mult; // теперь A для умножения
Result = A(3, 5); // вызов метода умножения Result=15
S = Add;
S+=Mult; // операции над делегатами
S-=Mult;
}
}
}
5) Виртуальный деструктор и виртуальное наследование в C++
Если класс полиморфен, желательно деструктор объявлять виртуальным. Если нет, возможна некорректная очистка памяти.
class Record
{
~Record(); // простой деструктор
..............
};
class Sprint:public Record
{
~Sprint();
.....
};
метод:
void F(Record *a)
{
.......
delete a;
}
Пусть указатель a указывает на производный класс, но вызовется только деструктор базового класса. виртуальный деструктор ккак положено, сначала производного класса, потом базового.
Пример множественного наследования.
Пусть в классе Record имеется поле weight – вес, который поднял штангист и
такое же поле в классе Person – собственный вес человека.
class Record
{
public:
int weight; // вес штанги
.......
}
class Person
{
public:
char Fam[30];
int Vozrast;
int weight; // вес спортсмена
......
}
class Sportsman : public Person , public Record // наследуются оба веса
{
public:
void Putweight(int x);
private:
int Category; // разряд спортсмена
............
}
Создаем класс Gravity с полем weight (вес).
class Gravity
{
protected:
int weight;
.........
};
Record и Person наследуются от Gravity
class Record:public Gravity
{
protected:
int min,sec;
.......
}
class Person:public Gravity
{
protected:
char Fam[30];
int Vozrast;
......
}
Теперь Sportsman наследуется от Person и Record
class Sportsman : public Person , public Record
{
public:
void Putweight(int x);
private:
int Category;
............
}
В памяти Sportsman сначала идут поля и методы Person, потом Record, потом собственные.
Поэтому в Sportsman 2 поля weight!!
Проблему решает виртуальное наследование.
class Record:virtual public Gravity
{
protected:
int min,sec;
.......
}
class Person:virtual public Gravity
{
protected:
char Fam[30];
int Vozrast;
......
}
В Sportsman одна копия weight.