Выбери формат для чтения
Загружаем конспект в формате pdf
Это займет всего пару минут! А пока ты можешь прочитать работу в формате 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
1
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()
2
{
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;
}
3
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;
4
}
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:
5
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;
6
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[] )
7
{
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);
8
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) // добавлен метод сложения двух объектов
{
10
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
}
4) Виртуальный деструктор и виртуальное наследование в C++
Если класс полиморфен, желательно деструктор объявлять виртуальным. Если нет, возможна
некорректная очистка памяти.
class Record
{
~Record(); // простой деструктор
..............
};
class Sprint:public Record
{
~Sprint();
.....
};
метод:
void F(Record *a)
{
.......
delete a;
}
Пусть указатель a указывает на производный класс, но вызовется только деструктор базового
класса. виртуальный деструктор ккак положено, сначала производного класса, потом
базового.
Пример множественного наследования.
Пусть в классе Record имеется поле weight – вес, который поднял штангист и
такое же поле в классе Person – собственный вес человека.
12
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:
13
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.
14