close

Вход

Забыли?

вход по аккаунту

?

Лекция№12

код для вставкиСкачать
Лекция № 12.
Использование шаблонов языка С++.
Одно из последних новшеств в языке С++ - шаблоны (templates). Это новое понятие является основным средством, позволяющим считать язык С++ языком обобщенного программирования. Вы помните, как упростился программный код после использования функций. Однако при вызове функции следует придерживаться строго контроля для типов входящих и фактических параметров, что не всегда удобно. Ведь алгоритм сортировки целочисленного и вещественного массивов не отличается. Обобщить такие действия можно при помощи шаблонов функций.
Шаблоны функций.
Для использования одной и той же функции с различными типами входящих параметров нужно определять несколько перегруженных версий этой функции для каждого типа. Допустим, что требуется функция, возвращающая наибольшее из двух значений как типа int, так и типа double. Тогда придется написать две перегруженные функции.
int Max(int a, int b)
{ return a>b&a:b;}
double Max(double a, double b)
{ return a>b&a:b;}
теперь напишем шаблон функции - единственное определение, позволяющее обрабатывать значения типа int, double или любого другого подходящего типа. Такой шаблон выглядит следующим образом:
template <class T>T Max (T a, T b)
{ return a>b&a:b;}
В этом определении идентификатор Т является параметром типа. Всякий раз, когда его имя встречается в теле функции, оно заменяется на тип переменной или константы, передаваемой при вызове функции. Если программа вызывает функцию Max и передаст ей значение типа int, например, следующим образом
cout<<"Наибольшее из 5 и 20 - это "<<Max(5,20);
то компилятор автоматически сгенерирует версию функции, в которой идентификатор Т заменяется на int:
int Max(int a, int b)
{ return a>b&a:b;}
Таким образом, компилятор генерирует функции для каждого вызова, в котором указывается другой числовой тип данных, например double, short или float. Процесс генерации новой версии функции называется созданием экземпляра шаблона функции.
Синтаксическая конструкция в определении шаблона имеет вид
template <class имя_параметра_типа>
Для параметра типа можно использовать любой правильный идентификатор имени. В угловые скобки можно включать несколько параметров, через запятую.
Примечание. Не путайте понятия параметр функции и параметр типа.
Реализация механизма шаблонов осуществляется на этапе компиляции. Причем компилятор генерирует код конкретной функции только при ее фактическом вызове. Поэтому ошибки в тексте шаблона функции не могут быть найдены до вызова этой функции в тексте программы.
Повторные вызовы функции с идентичными параметрами, естественно, не приводят к генерации новых экземпляров функции. Компилятор генерирует отдельную версию функции, даже если типы параметра близки (например, long и int).
Используя шаблоны вместо перегруженных функций, можно не учитывать, к какой версии функции произойдет обращение в программе. Компилятор автоматически генерирует и сохраняет только те версии функции, которые будут фактически вызываться.
В нашем примере оба параметра должны быть одного типа. В противном случае компилятор не сумеет сопоставить параметру типа Т тип первого или второго параметра. Таким образом, допустимы такие вызовы функции
cout<<"Наибольшее из 20 и 50 - это"<<Max(20,5)<<'\n';
cout<<"Наибольшее из A и W - это"<<Max('A','W')<<'\n';
А следующий вызов является недопустимым
cout<<"Наибольшее из 25.5 и 20 - это"<<Max(25.5,20)<<'\n';
Компилятор в процессе генерации экземпляра шаблона не преобразует второй параметр int в double для приведения типов, хотя это преобразование является стандартным.
Чтобы передавать параметры различных типов, нужно определить шаблон с двумя параметрами типов.
template <class T1, class T2>T1 Max (T1 a, T2 b)
{ return t1(a>b&a:b);}
Для новой версии шаблона последний пример вызова функции является допустимым и печатает значение 25.5, так как тип возвращаемого значение приводится к типу Т1. Если тип возвращаемого значения преобразовать к типу Т2, то будет возвращено значение 25. Тот же результат будет, если организовать вызов функции с входящими параметрами в другом порядке
cout<<"Наибольшее из 25.5 и 20 - это"<<Max(20,25.5)<<'\n';
Каждый параметр типа, встречающийся внутри символов < >, должен также появляться в списке параметров функции. Следовательно, следующее определение шаблона недопустимо:
template <class T1, class T2>T1 Max (T1 a, T1 b)
{ return a>b&a:b;}
При таком определении компилятор, встретив вызов функции, не сможет определить значение идентификатора T2. Это - ошибка, даже если идентификатор Т2 нигде не использован.
Определение шаблонов классов.
Рассмотрим простой пример класса для хранения 100 целочисленных значений.
class IntMas{
public:
IntMas();
void SetBuf( const int * m);
Int * GetBuf() const;
private:
int Buf[100];
};
Для хранения чисел используется закрытый массив Buf. Функция - член SetBuf() присваивает значения всех элементам, а GetBuf получает значения всех элементов, функция SetIndex присваивает значения заданному элементу, а функция GetIndex возвращает значение элемента с указанным индексом.
Задача хранения множества значений является очень типичной. Поэтому полезными являются подобные классы для хранения вещественных значений, структур или большего количества элементов (например, 2000). Однако в этих случаях придется определять полностью новые класса. Проблему нельзя решить созданием производного класса от IntMas, так как тип и количество хранимых элементов нельзя изменять в производных классах.
Вместо этого можно написать единственный шаблон класса, который будет автоматически генерировать целое семейство классов для хранения данных различных типов и различного числа элементов.
template <class T, const int n> class CMas{
public:
void SetBuf (const T * m);
T * GetBuf () const;
//const обязательно иначе нельзя передать константу
void SetIndex(int In,const T& p);
int GetIndex(int In) const;
private:
T Buf[n];
};
В этом определении T является параметром типа, а n - параметром - константой. Фактические значения для параметров Т и n будут устанавливаться при создании определенного экземпляра класса. В шаблон класса можно включить список из любого числа параметров, ограниченный символами "<" ">"(список должен содержать хотя бы один параметр). Параметры - константы могут иметь любой допустимый тип.
Следующий необходимый этап - определение функций - членов шаблона класса. Функцию - член SetBuf можно определить следующим образом.
template <class T, const int n> void CMas<T,n>::
SetBuf(const T * m)
{ for(int i=0;i<n;i++)
Buf[i]=m[i];
}
Определение должно начинаться спецификатором template, за которым следует такой же список параметров в угловых скобках, как и в определении шаблона класса (template <class T, const int n>). За именем класса, предшествующим операции разрешения области видимости, должен следовать список имен параметров шаблона <T,n>. По аналогии можно определить другие функции - члены.
template <class T, const int n> T * CMas<T,n>::
GetBuf() const
{ return Buf; }
template <class T,const int n> void CMas<T,n>::
SetIndex(int In, const T& p)
{
Buf[In]=p;
}
template <class T,const int n> int CMas<T,n>::
GetIndex(int In) const
{
return Buf[In];
}
Создание объектов на основании шаблона.
Шаблоны классов предназначены не для порождения семейства однотипных классов, а для создания на основании единственного определения (шаблона) семейства однотипных объектов.
При создании объекта по шаблону класса нужно точно определить значения параметров шаблона. Можно создать экземпляр шаблона класса CMas для хранения не более 100 целых значений.
CMas <int, 100> IntMas;
В результате будет сгенерирован объект IntMas как экземпляр версии CMas. В нем каждое вхождение параметра Т заменяется типом int, а каждое вхождение параметра n - константой 100. В результате Buf будет массивом из 100 значений типа int, а функции - члену SetBuf будет передан указатель на тип int.
Теперь можно вызвать метод SetIndex.
IntMas.SetIndex(5,6);
Чтобы создать объект для хранения списка строк, экземпляр CMas можно определить так
CMas <char *,25> StringList;
Тогда для присваивания строки какому-либо элементу списка следует в функции SetIndex передавать второму параметру указатель на массив типа char.
StringList.SetIndex(0,"Alla");
Определим указатель на объект для хранения списка значений типа double.
CMas <double,25>*DoubleMas;
DoubleMas=new CMas <double,25>;
При определении типа указателя DoubleMas, так же как и при задании типа в операторе new, вместе с именем шаблона следует включить список его параметров <double,25>. Список параметров шаблона является неотъемлемой частью определения типа.
Можно создавать объекты для хранения структур, объединений или классов при условии, что они определены глобально. Например, в следующем фрагменте создается объект для сохранения структур.
//определение структуры City на глобальном уровне:
struct City{
char Name[25];
long Area;
};
void main()
{ //создание объекта для хранения списка, содержащего //не более 50 структур типа City
CMas <City,50> CityMas;
//создание и инициализация экземпляра структуры City
City Rec={"Odessa",3287};
//копирование содержимого Rec в первый элемент списка
CityMas.SetIndex(0,Rec);
}
Определение конструкторов в шаблоне.
Для инициализации элементов массива в шаблон CMas следует добавить конструктор. Чтобы инициализировать массив Buf, конструктор должен получать параметр типа Т. Конструктор можно реализовать так:
template <class T,const int n> CMas<T,n>::
CMas(T InitValue)
{
for(int i=0;i<n;i++)
Buf[i]=InitValue;
}
Тогда создание рассмотренных объектов должно иметь вид:
CMas <int, 100> IntMas(0);
IntMas.SetIndex(5,6);
CMas <char *,25> StringList("");
StringList.SetIndex(0,"Alla");
City Rec={"",0};
CMas <City,50> CityMas(Rec);
В определение класса CMas нужно включить конструктор без входящих параметров иначе не будет работать строка
CMas <double,25>*DoubleMas;
DoubleMas=new CMas <double,25>;
Аналогично конструктору определяется деструктор для шаблона класса. При этом используется синтаксис, подобный синтаксису конструктора. Например, деструктор класса CMas объявлен в определении шаблона как
template <class T,const int n> CMas<T,n>::
~CMas()
{}
Окончательное определение класса CMas имеет вид:
template <class T, const int n> class CMas{
public:
CMas(T InitValue);
CMas() {};
~CMas();
void SetBuf (const T * m);
T * GetBuf () const;
//const обязательно иначе нельзя передать константу
void SetIndex(int In,const T& p);
int GetIndex(int In) const;
private:
T Buf[n];
};
template <class T,const int n> CMas<T,n>::
CMas(T InitValue)
{
for(int i=0;i<n;i++)
Buf[i]=InitValue;
}
template <class T,const int n> CMas<T,n>::
~CMas()
{}
template <class T, const int n> void CMas<T,n>::
SetBuf(const T * m)
{ for(int i=0;i<n;i++)
Buf[i]=m[i];
}
template <class T, const int n> T * CMas<T,n>::
GetBuf() const
{ return Buf; }
template <class T,const int n> void CMas<T,n>::
SetIndex(int In, const T& p)
{
Buf[In]=p;
}
template <class T,const int n> int CMas<T,n>::
GetIndex(int In) const
{
return Buf[In];
}
struct City{
char Name[25];
long Area;
};
int main()
{
CMas <int, 100> IntMas(0);
IntMas.SetIndex(5,6);
CMas <char *,25> StringList("");
StringList.SetIndex(0,"Alla");
CMas <double,25>*DoubleMas;
DoubleMas=new CMas <double,25>;
City Rec={"",0};
CMas <City,50> CityMas(Rec);
return 0;}
Переопределение шаблонов.
Механизм шаблонов обеспечивает одинаковую обработку для каждого значения параметров типа. Чтобы обеспечить специальную обработку для отдельного параметра (или параметров) типа, можно определить обычную функцию языка С++ с тем же именем, что и шаблон функции. Когда компилятор обнаруживает, что в вызове функции типы переданных параметров соответствуют спецификации обычной функции, то он вызовет ее, а не сгенерирует функцию по шаблону.
В качестве примера можно определить новую версию функции Max, которая будет работать с экземплярами класса kvadrat. Эта функция будет сравнивать два объекта класса квадрат по члену - данному razmer.
kvadrat Max(kvadrat A,kvadrat B)
{
if (A.GetRazmer()>B.GetRazmer())
return A;
else
return B;
}
Если программа вызовет функцию Max, передав ей в качестве входящих параметров - два объекта класса kvadrat, то компилятор инициализирует вызов приведенной функции вместо создания экземпляра функции по шаблону Max.
7
Документ
Категория
Разное
Просмотров
12
Размер файла
68 Кб
Теги
лекция
1/--страниц
Пожаловаться на содержимое документа