Шаблон класса
Шаблоны позволяют определить конструкции (функции, классы), которые используют определенные типы, но на момент написания кода точно не известно, что это будут за типы. Иными словами, шаблоны позволяют определить универсальные конструкции, которые не зависят от определенного типа.
Шаблон класса (class template) позволяет задать тип для объектов, используемых в классе. Но прежде чем перейти к определению шаблона класса, рассмотрим проблему, с которой мы можем столкнуться и которую позволяют решить шаблоны.
Допустим, нам надо описать класс банковского счета. Банковский счет должен иметь номер, однако на момент написания класса может быть неизвестно, какой тип будет представлять номер счета - это может быть числовой номер 1233545, а может и быть просто набор символов в виде строки, например, "1234878rtyio". На первый взгляд, мы можем просто определить два класса:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <string>
class IntAccount {
private:
int id;
public:
IntAccount(int id) : id(id)
{ }
int getId() {
return id;
}
};
class StrAccount {
private:
std::string id;
public:
StrAccount(std::string id) : id(id)
{ }
std::string getId() {
return id;
}
};
int main()
{
StrAccount acc1("ca-pub1343767");
IntAccount acc2(1234566);
std::cout << "acc1: " << acc1.getId() << std::endl;
std::cout << "acc2: " << acc2.getId() << std::endl;
return 0;
}
Хотя данный пример работает, но по сути мы получаем два идентичных класса, которые отличаются только типом id. Шаблоны класса позволяют уменьшить повторяемость кода, задав для класса универсальный тип. Изменим код, применив шаблоны:
1
2
3
4
5
6
7
8
9
10
11
template <typename T>
class Account {
private:
T id;
public:
Account(T id) : id(id)
{ }
T getId() {
return id;
}
};
Для применения шаблонов перед классом указывается ключевое слово template, после которого идут угловые скобки. В угловых скобках после слова typename идет параметр шаблона. Можно определить несколько параметров шаблона, в примере выше применяется только один параметр.
Параметр шаблона представляет произвольный идентификатор, в качестве которого, как правило, применяюся заглавные буквы, например, T. Но это необязательно. То есть в данном случае параметр T будет представлять некоторый тип, который становится известным во время компиляции. Это может быть и тип int, и double, и string, и любой другой тип. И теперь идентификатор счета будет представлять тип, который передается через параметр T.
Используем этот класс:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <string>
template <typename T>
class Account {
private:
T id;
public:
Account(T id) : id(id)
{ }
T getId() {
return id;
}
};
int main()
{
Account<std::string> acc1("ca-pub1343767");
Account<int> acc2(1234566);
std::cout << "acc1: " << acc1.getId() << std::endl;
std::cout << "acc2: " << acc2.getId() << std::endl;
return 0;
}
При использовании шаблона класса необходимо в угловых скобках после названия класса указать конкретный тип, который будет применяться вместо параметра T. Так, в первом случае вместо T применяется тип string, поэтому в конструктор класса можно передать строку:
1
Account<std::string> acc1("ca-pub1343767");
Во втором случае применяется тип int, поэтому в конструктор передается число:
1
Account<int> acc2(1234566);
Также можно применять сразу несколько параметров. Например, необходимо определить класс банковского перевода:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <string>
template <typename T, typename V>
class Transaction
{
public:
Transaction(T fromAcc, T toAcc, V code, int sum):
fromAccount(fromAcc), toAccount(toAcc), code(code), sum(sum)
{ }
void getInfo()
{
std::cout << "From: " << fromAccount << "\nTo: " << toAccount
<< "\nSum: " << sum << "\nCode: " << code << std::endl;
}
private:
T fromAccount; // с какого счета
T toAccount; // на какой счет
V code; // код операции
int sum; // сумма перевода
};
int main()
{
Transaction<std::string, int> t1("id1234", "id5678", 2804, 5000);
t1.getInfo();
return 0;
}
Класс Transaction использует два параметра типа T и V. Параметр T определяет тип для счетов, которые участвуют вв процессе перевода. Здесь в качестве номеров счетов можно использовать и числовые и строковые значения и значения других типов. А параметр V задает тип для кода операции - опять же это может быть любой тип.
При использовании шаблона в этом случае надо указать два типа:
1
Transaction<std::string, int> t1("id1234", "id5678", 2804, 5000);
Типы передаются параметрам по позиции. Так, тип string будет использоваться вместо параметра T, а тип int - вместо параметра V.