Разделы презентаций


Шаблоны

Содержание

#include using namespace std;void print(int *array,int d){ for (int i=0; i < d; cout

Слайды и текст этой презентации

Слайд 1В языке С++ есть еще один вид полиморфизма, который носит

название статического полиморфизма и представляется через механизм шаблонов (templates). Шаблоны

используются для создания параметризованных типов (обычно классов) и параметризованных функций.

Шаблоны

Итак, два вида шаблонов – шаблоны функций и шаблоны классов.
Можно написать функцию и, в зависимости от аргументов, будет обеспечено исполнение того или иного тела функции. А что, если в роли аргументов функции мы хотим видеть тип данных?
Раньше с такой целью использовались макросы. Именно с такой целью позднее в язык С++ были введены шаблоны.

В языке С++ есть еще один вид полиморфизма, который носит название статического полиморфизма и представляется через механизм

Слайд 2#include
using namespace std;

void print(int *array,int d)
{ for (int

i=0; i < d; cout

}

void print(char *array,int d)
{ for (int i=0; i < d; cout << array[i],i++); cout << endl; }

void main()
{
char *a1 ="Thing!";
int a2[5] = {1,2,3,4,5};
print (a1,6);
print (a2,5);
char s; cin >> s;
}

Шаблоны функций – пример потребности

#include using namespace std;void print(int *array,int d){  for (int i=0; i < d; cout

Слайд 3#include
using namespace std;

#define PRN(Type,arr,d) \
{ for (int i=0; i

< d; cout

main()
{
char *a1 ="Thing!";
int a2[5] = {1,2,3,4,5};
PRN(char, a1, 6); // print (a1,6);
PRN(int, a2, 5); // print (a2,5);

char s; cin >> s;
}

Применение макросов

#include using namespace std;#define PRN(Type,arr,d) \{ for (int i=0; i < d; cout s;}Применение макросов

Слайд 4Шаблоны функций являются логическим продолжением механизма перегрузки функций. Обычно перегрузку

функций осуществляют для различных типов данных, причем все перегруженные функции

осуществляют идентичные действия. Например, для сортировки и вывода на экран одномерных массивов данных различных типов, приходится реализовывать различные перегруженные функции, содержимое которых порой отличается только используемыми типами данных. Так для возможности сортировки массивов int* и char* приходится реализовывать две перегруженные функции sort(int*, int) и sort(char*, int).
Механизм шаблонов в С++ позволяет решить эту проблему более удобным способом. Достаточно описать один шаблон, а компилятор сгенерирует код такой функции в соответствии использованному типу данных при вызове такой функции.

Шаблоны функций

Шаблоны функций являются логическим продолжением механизма перегрузки функций. Обычно перегрузку функций осуществляют для различных типов данных, причем

Слайд 5Шаблон функции задается следующим образом:

template возвращаемый_тип имя_функции (аргументы)
{

. . . }

Определение каждого типа параметра начинается с ключевого

слова class. Если функция использует более одного типа, то все они перечисляются через запятую. При компиляции компилятор вместо типа параметра подставляет необходимый тип, который был задан при вызове функции.
В качестве типа параметра могут быть заданы не только встроенные типы, но и типы, определенные пользователем, в том числе и классы.

Шаблоны функций – определение

Шаблон функции задается следующим образом:template  возвращаемый_тип имя_функции (аргументы){ . . . }Определение каждого типа параметра начинается

Слайд 6Синтаксис шаблона должен повторяться как при объявлении, так и при

определении функции.
Помните, шаблоны на самом деле являются макросами, поэтому

они должны находиться в файлах .h. Если определение будет находиться в файле .срр, то в других .cpp-файлах нашей программы шаблон будет недоступен и программа работать не будет.

// Объявление функции
template
Type* func(Type* t);
 

Шаблоны функций – определение дополнительно

// Вызов параметризированной функции
Obj* f = func(new Obj);
//Определение генерируется компилятором
// при необходимости, то есть при вызове функции.

// Определение ее реализации
template
Type* func(Type* t) {
// Тело функции, в котором имя
// Type используется в качестве
// параметра макроса
}
 

Синтаксис шаблона должен повторяться как при объявлении, так и при определении функции. Помните, шаблоны на самом деле

Слайд 7Для нашего примера можем определить:

template void print (Array_Type

* array, int d)
{ for (int i=0; i

d; cout << array[i],i++); cout << endl; }

И тогда
void main()
{
char *a1 ="Thing!";
int a2[5] = {1,2,3,4,5};
long a3[3] = {0xffffeeee, 0xeeeeaaaa, 0xcccccccc}
print (a1,6);
print (a2,5);
print (a3,3);
char s; cin >> s;
}


Шаблоны функций – пример

Для нашего примера можем определить:template void print (Array_Type * array, int d){  for (int i=0; i

Слайд 8#include
using namespace std;

class Integer {
int n;
public:

Integer(int n=0) { this->n = n; }
int get()

{ return n; }
};

class String {
char * str;
public:
String(char *str="No") { this->str = str; }
char * get() { return str; }
};


Шаблоны функций – еще пример

template
void print_info (Class_Type obj)
{
cout << obj.get() << endl;
}


void main()
{
Integer Num(12);
String buf;
print_info (Num);
print_info (buf);

char s; cin >> s;
}



Слайд 9Шаблоны функций могут быть перегружены.
Если в шаблоне описаны несколько типов

параметров, то они все должны быть обязательно описаны в списке

аргументов функции.
template
void print_info (Class_Type1 obj1, Class_Type2 obj2)
{
cout << " Obj1 = " << obj1.get() << endl;
cout << " Obj2 = " << obj2.get() << endl;
}
void main()
{
Integer Num(12); String buf;
print_info (Num, buf); cout << endl;
print_info (buf, Num);
char s; cin >> s;
}

Шаблоны функций – дополнительно


Результаты:

Шаблоны функций могут быть перегружены.Если в шаблоне описаны несколько типов параметров, то они все должны быть обязательно

Слайд 10Идея параметризации классов та же – описывается шаблон класса, который

осуществляет идентичные действия с различными типами данных. Используемый тип данных

определяется на стадии компиляции.
Объявление параметризированного класса (шаблона):
template
объявление параметризируемого класса;
Тип является аргументом шаблона.
Экземпляр параметризированного класса описывается в виде:
имя_класса < тип > объект;
Варианты использования:
stack stk_c; // стек на 1000 символов
stack stk_str(200); // стек на 200 указателей
stack stk_cx(40); // стек на 40 комплексных чисел

Определение шаблона класса

Идея параметризации классов та же – описывается шаблон класса, который осуществляет идентичные действия с различными типами данных.

Слайд 11template
class stack {
public:
explicit stack( int size

= 1000) : max_len (size)
{s = new TYPE[size];

top = EMPTY; }
~stack( ) { delete [ ] s; }
void reset( ) { top = EMPTY; } // очистить стек
void push(TYPE c) { s[++top] = c; } // записать в стек
TYPE pop( ) { return s[top - -]; } // извлечь элемент
TYPE top_of ( ) { returns[top]; } // считать элемент
bool empty( ) { return (top == EMPTY); } // пуст?
bool full( ) { return (top == max_len-1); } // полон?
private:
enum {EMPTY = -1}; // перечислимый тип данных
TYPE* s; // тело стека
int max_len; // максимальный размер
int top; // вершина стека
};

Пример шаблона stack:

template class stack {public: explicit stack( int size = 1000) : max_len (size)  {s = new

Слайд 12template
class ListNode {
private:
ListNode* next;


Type* data;
public:
ListNode(Type* d, ListNode* n =

NULL) : next(n), data(d) {}
~ListNode() { delete next; }
Type* Data() { return data; }
ListNode* Next() { return next; }
};
. . .
ListNode list = new ListNode (new Obj);
Obj* f = list->Data(); // Возвращает правильный тип

Пример шаблона «Узел списка»:

template class ListNode { private: ListNode* next; Type* data; public: ListNode(Type* d, ListNode* n = NULL) :

Слайд 13template
class ListNode {
private:
ListNode next;


Type* data;
public:
ListNode(Type* d, ListNode* n =

NULL);
~ListNode();
Type* Data();
ListNode* Next();
};

// Все тоже самое, только функции лишь объявлены, но не определены

Параметризованные функции классов -1

template class ListNode { private: ListNode next; Type* data; public: ListNode(Type* d, ListNode* n = NULL); ~ListNode();

Слайд 14template
ListNode::ListNode(Type* d, ListNode* n = NULL)

: next(n), data(d) { }

template
ListNode::~ListNode() {

delete next; }

template
Type* ListNode::Data() { return data; }

template
ListNode* ListNode::Next() { return next; }

Помните: все это должно находиться в файле .h. Исключение составляют ситуации, когда функции класса вызываются только из того же файла, в котором определяются. В этом случае определения функций класса должны предшествовать их первому использованию.

Параметризованные функции классов -2

template ListNode::ListNode(Type* d, ListNode* n = NULL) : next(n), data(d) { } template ListNode::~ListNode() { delete next;

Слайд 15Шаблонный конструктор представляет собой особую разновидность шаблона, определяемого внутри класса.

Обычно определяется для обеспечения неявных преобразований типов при копировании объектов.


Шаблонный конструктор не замещает стандартный копирующий конструктор! При точном соответствии типов будет сгенерирован и вызван стандартный конструктор копии.


Шаблонный конструктор

template
class MyClass {
public:
template
MyClass ( const MyClass &x);
. . .
};

void Func ( )
{
MyClass xd;
. . .
MyClass xd2 (xd);
MyClass xi (xd); // ?=
};

Если вы определяете шаблонный конструктор, не забудьте определить и конструктор копии, если его стандартное поведение вас не устраивает!

Шаблонный конструктор представляет собой особую разновидность шаблона, определяемого внутри класса. Обычно определяется для обеспечения неявных преобразований типов

Слайд 16При явном вызове конструктора без аргументов базовые типы инициализируются нулями.

int

i1; // Неопределенное значение
int

i2 = int(); // Переменная инициализируется нулем

Такая возможность была реализована для того, чтобы было можно написать код шаблона, в котором величине любого типа заведомо будет присвоено некоторое значение по умолчанию.
Например, в следующей функции вызов конструктора гарантирует, что для всех базовых типов переменная x будет инициализирована нулем:

Явная инициализация базовых типов

template
void F()
{
T x = T();
. . .
}

При явном вызове конструктора без аргументов базовые типы инициализируются нулями.int i1;

Слайд 17Многочисленные символы < и > вызывают изрядную путаницу, поскольку C++

не всегда последователен.
следует указывать везде, кроме трех мест

в объявлениях классов или определениях их функций:
1. За ключевым словом class в самом начале.
2. При указании имени конструктора.
3. При указании имени деструктора.
Аргументы конструкторов и деструкторов должны быть параметризо-ванными, как и все использования имени класса за исключением трех указанных случаев. При любом использовании параметризованного типа или функции необходимо указывать параметр.
В трех указанных ситуациях компилятор может сделать разумные предположения по поводу отсутствующих символов.

Передача параметра

Многочисленные символы < и > вызывают изрядную путаницу, поскольку C++ не всегда последователен.  следует указывать везде,

Слайд 18template
class ListNode {
private:
ListNode next;


Type* data;
public:
ListNode(Type* d, ListNode* n =

NULL);
~ListNode();
Type* Data();
ListNode* Next();
};

Где не надо ?

template class ListNode { private: ListNode next; Type* data; public: ListNode(Type* d, ListNode* n = NULL); ~ListNode();

Слайд 19«Тип может иметь более одного параметра, хотя такие ситуации встречаются

довольно редко. Увидев такой класс, я обычно затеваю с автором

долгую беседу о принципах построения программ. Тем не менее, иногда использование многоаргументных шаблонов бывает оправданным.
Синтаксис выглядит аналогично, разве что вместо используется список типа .
Наконец, параметр не обязан быть классом. Он может быть структурой или еще чем-нибудь, хотя именно классы прочно удерживают ведущие позиции на рынке параметров. »

(из книги Джефф Элджер. С++. Библиотека программиста)

А если несколько параметров?

«Тип может иметь более одного параметра, хотя такие ситуации встречаются довольно редко. Увидев такой класс, я обычно

Слайд 20template
class clt {

static int Nobj;

Type element;

public:
clt (Type e) { element

= e; };
Type& get(){ return element; }

friend ostream& operator << (ostream &os, clt & Obj)
{
return os << "Element " << Nobj++ << "=" << Obj.element << endl;
}
};

Шаблоны классов со статическими членами

template class clt { static int Nobj; Type element; public:   clt (Type e) { element

Слайд 21Результаты:

int clt::Nobj = 0;
int clt::Nobj = 0;
int clt::Nobj = 0;
void

main()
{
clt Num(5);
clt Str("Message");
clt

dbl(5.9);
cout << Num << Str;
Num.get()++;
Str.get() = "Hi-hi";
cout << Num << Str;
clt Obj(12);
cout << Obj << dbl;
char s; cin >> s;
}

Шаблоны классов со статическими членами

Результаты:int clt::Nobj = 0;int clt::Nobj = 0;int clt::Nobj = 0;void main() {  clt Num(5);  clt

Слайд 22Параметризованные классы могут быть производными от других классов, параметризованных или

нет.
Параметризованный класс может выступать в качестве базового; в этом

случае производный класс также будет параметризованным с тем же форматом аргументов, что и в базовом классе.
В производном классе могут добавляться новые параметры, но разбираться в полученной куче вам надоест уже с третьей строки…
Чаще всего встречается наследование параметризованного типа от непараметризованного.

Наследование

Параметризованные классы могут быть производными от других классов, параметризованных или нет. Параметризованный класс может выступать в качестве

Слайд 23 Рассмотрим 3 ситуации:
1) Предположим, у вас имеется параметризованный класс, реализация

всех функций которого занимает 1000 строк. При каждом его использовании

для нового типа компилятор радостно выплевывает очередные 1000 строк расширенного кода. Это слишком высокая цена за безопасность типа.
2) Допустим, вы продаете библиотеку классов и не хотите поставлять исходный текст, а только интерфейсы. Если библиотека содержит параметризованные функции, они должны находиться в открытом для всего мира файле .h. Обидно.
3) Допустим, кто-то передает вам замечательный, но небезопасный по отношению к типам класс или библиотеку классов. Может быть, он был написан на компиляторе, который не поддерживает шаблоны, или автор просто не верит в шаблоны. Вам хочется подправить код и сделать его безопасным с помощью шаблонов. Но хотите ли вы переделывать все подряд, включать весь код реализации в файлы .h и добавлять в объявления класса параметры и символы <>?

Комбинации простых и параметризованных типов

Рассмотрим 3 ситуации:1) Предположим, у вас имеется параметризованный класс, реализация всех функций которого занимает 1000 строк. При

Слайд 24Во всех описанных ситуациях стоит использовать параметризованный тип в сочетании

с простым, непараметризованным типом.
Когда это будет сделано, в 99

случаях из 100 параметризованный тип «заворачивает» простой тип в симпатичную, мягкую и безопасную по отношению к типам оболочку.
При этом простой класс не изменяется — просто параметризованный класс помещается между небезопасным классом и пользователем.


Комбинации простых и параметризованных типов

Во всех описанных ситуациях стоит использовать параметризованный тип в сочетании с простым, непараметризованным типом. Когда это будет

Слайд 25
Небезопасные типы в открытых базовых классах - это не серьезно.


Если вы попытаетесь ввести безопасность типов в производном классе с

открытым наследованием, клиент получит полный доступ ко всем небезопасным средствам базового класса. Существуют сложные невероятно изобретательные решения этой проблемы, но в любом случае у вас выйдет что-то вроде подвесного моста из бутылочных пробок: гениальная работа при плохом материале.

(из книги Джефф Элджер. С++. Библиотека программиста)

Небезопасные типы в открытых базовых классах

Небезопасные типы в открытых базовых классах - это не серьезно. Если вы попытаетесь ввести безопасность типов в

Слайд 26Самый простой способ обеспечить безопасность типов — сделать ненадежный класс

закрытым базовым классом безопасного шаблона.
Небезопасные типы в закрытых базовых

классах

class UnsafeNode { // ListNode из предыдущего примера
private:
UnsafeNode* next;
void* data;
public:
UnsafeNode(void* d, UnsafeNode* n);
virtual ~UnsafeNode();
UnsafeNode* Next();
void* Data();
};
template
class SafeNode : private UnsafeNode {
public:
SafeNode(Type* d, SafeNode* n) : UnsafeNode(d, n) {}
virtual ~SafeNode() { delete (Type*)Data(); }
SafeNode* Next() { return (SafeNode*)UnsafeNode::Next(); }
Type* Data() { return (Type*)UnsafeNode::Data(); }
};

Базовый класс недоступен для клиентов производного шаблона

Самый простой способ обеспечить безопасность типов — сделать ненадежный класс закрытым базовым классом безопасного шаблона. Небезопасные типы

Слайд 27Ключевое слово typename означает, что следующий за ним идентификатор обозначает

тип. Рассмотрим пример:

Ключевое слово typename
template
class MyClass {
typename SubType

* ptr;
. . .
};

В этом примере ключевое слово typename поясняет, что SubType является подтипом класса T. Тогда ptr – указатель на тип T::SubType.
Без typename идентификатор SubType интерпретируется как статический член класса, а следующая строка воспринимается

как умножение значения SubTyp типа T на ptr: T::SubType * ptr .
Поскольку идентификатор SubType объявлен типом, любой тип, используемый вместо T, должен иметь в себе внутренний тип SubType.
Например, использование типа Q в
качестве аргумента шаблона, возможно
только при условии, что в Q определен
внутренний тип SubType:

MyClass x;
// Объявление класса
class Q {
typedef int SubType;
. . .
};

Ключевое слово typename означает, что следующий за ним идентификатор обозначает тип. Рассмотрим пример:Ключевое слово typenametemplate class MyClass

Слайд 28В рассмотренном примере переменная ptr является указателем на int. В

общем случае подтип может быть абстрактным типом данных (например классом).

В этом случае:
class Q {
class SubType;
. . .
};
Ключевое слово typename всегда должно квалифицировать идентификатор шаблона как тип, даже если другая интерпретация не имеет смысла. В С++ любой идентификатор шаблона, указанный без ключевого слова typename, интерпретируется как значение.
Ключевое слово typename также может использоваться вместо слова class в определении шаблона:
template
class clt {
. . .
};


Еще о typename

В рассмотренном примере переменная ptr является указателем на int. В общем случае подтип может быть абстрактным типом

Слайд 29В качестве параметров шаблонов могут выступать не только типы. Однако

такой нетипизированный параметр все равно считается частью определения типа.
Так

например, аргумент шаблона стандартного класса bitset <> содержит количество входящих в него бит.

bitset <32> field32; // поле на 32 бита
bitset <50> field50; // поле на 50 бит

Эти поля будут относится к разным типам, поскольку при их определении задаются разные значения аргументов шаблона. Следовательно, их нельзя присваивать или сравнивать друг с другом (при отсутствии соответствующего преобразования типа).

Нетипизированные параметры шаблонов

В качестве параметров шаблонов могут выступать не только типы. Однако такой нетипизированный параметр все равно считается частью

Слайд 30Шаблоны классов могут иметь параметры по умолчанию.
Следующий пример разрешает

объявлять объекты класса MyClass как с одним, так и с

двумя аргументами.

template >
class MyClass . . .

При передаче одного аргумента вместо второго используется параметр по умолчанию:
MyClass x1; // Эквивалентно: MyClass > x1;

Аргументы шаблонов по умолчанию могут определяться на основе предыдущих аргументов.

Параметры шаблонов по умолчанию

Шаблоны классов могут иметь параметры по умолчанию. Следующий пример разрешает объявлять объекты класса MyClass как с одним,

Слайд 31Тесты …
Вопрос: Что выведет следующая программа:
#include
using namespace std;
 
class

a {
private:
virtual void f(int) { cout

b : public a {
public:
class a; friend
void f(double) { cout<<"b double"; }
void f(int) { cout<<"b int"; }
};
 
int main (void) {
b* o = new b;
o->f(0.5);
return 0;
}

Варианты ответа:

возникнет ошибка компиляции
b int
b double
a int

Тесты …Вопрос: Что выведет следующая программа: #include using namespace std; class a {private:  virtual void f(int) {

Слайд 32Тесты …
Вопрос: Что выведет следующая программа:
#include
using namespace std;
 
class

a {
private:
virtual void f(int) { cout

b : public a {
public:
class a; friend
void f(double) { cout<<"b double"; }
void f(int) { cout<<"b int"; }
};
 
int main (void) {
b* o = new b;
o->f(0.5);
return 0;
}

Варианты ответа:

возникнет ошибка компиляции
b int
b double
a int


В класса b директива friend относится к f(double), т.е. она дружественная функция (не член класса), хоть и определена в классе. В main() идет вызов o->f(), т.е. функции, являющейся ЧЛЕНОМ КЛАССА b, т.е. функции b::f(int).

Тесты …Вопрос: Что выведет следующая программа: #include using namespace std; class a {private:  virtual void f(int) {

Слайд 33Тесты …
Вопрос: Что выведет следующая программа:
#include
class A
{

public:
A() { }

operator int() { return 10; };
operator float() const { return 2.0; };
};
class B
{
public:
B() { }
operator int() const { return 5; };
operator float() { return 1.0; };
};
int main()
{
A a;
B b;
std::cout << a + b << std::endl;
return 0;
}

Варианты ответа:

Код не корректен: Выбор операторов приведения для операндов сложения не определен в данном контексте
7
Код не корректен по другой причине
15
11
3

Тесты …Вопрос: Что выведет следующая программа: #include class A{   public:

Слайд 34Тесты …
Вопрос: Что выведет следующая программа:
#include
class A
{

public:
A() { }

operator int() { return 10; };
operator float() const { return 2.0; };
};
class B
{
public:
B() { }
operator int() const { return 5; };
operator float() { return 1.0; };
};
int main()
{
A a;
B b;
std::cout << a + b << std::endl;
return 0;
}

Варианты ответа:

Код не корректен: Выбор операторов приведения для операндов сложения не определен в данном контексте
7
Код не корректен по другой причине
15
11
3

Смотреть необходимо не на типы описанных операторов приведения, а на их константность. Для неконстантных экземпляров будут выбраны они.


Тесты …Вопрос: Что выведет следующая программа: #include class A{   public:

Слайд 35Тесты …
Вопрос: Что выведет следующая программа:
#include
 
class A {
public:

virtual A &Get() {
std::cout

*this;
}
};
struct B : A {
B &Get() {
std::cout << "B“; return *this;
}
};
int main(int, char *argv[]) {
B b;
A &a1(b), a2(a1), *a3(&a2), *a4(&a1);
b.Get();
a1.Get();
a2.Get();
a3->Get();
a4->Get();
}
Тесты …Вопрос: Что выведет следующая программа: #include  class A {public: virtual A &Get() {  std::cout Get();

Слайд 36Тесты …
Вопрос: Что выведет следующая программа:
#include
 
class A {
public:

virtual A &Get() {
std::cout

*this;
}
};
struct B : A {
B &Get() {
std::cout << "B“; return *this;
}
};
int main(int, char *argv[]) {
B b;
A &a1(b), a2(a1), *a3(&a2), *a4(&a1);
b.Get();
a1.Get();
a2.Get();
a3->Get();
a4->Get();
}

BBAAB

Тесты …Вопрос: Что выведет следующая программа: #include  class A {public: virtual A &Get() {  std::cout Get();

Обратная связь

Если не удалось найти и скачать доклад-презентацию, Вы можете заказать его на нашем сайте. Мы постараемся найти нужный Вам материал и отправим по электронной почте. Не стесняйтесь обращаться к нам, если у вас возникли вопросы или пожелания:

Email: Нажмите что бы посмотреть 

Что такое TheSlide.ru?

Это сайт презентации, докладов, проектов в PowerPoint. Здесь удобно  хранить и делиться своими презентациями с другими пользователями.


Для правообладателей

Яндекс.Метрика