2. Два
вида
многократного
использования
кода
Наследование
• Создаем
структуру
для
работы
с
«базовым
классом»
• Создаем
классы-‐наследники
на
каждый
случай.
Шаблоны
• Описываем
«стратегию
работы»
с
«неопределенным»
классом.
• Компилятор
в
момент
создание
класса
по
шаблону,
сам
создает
нужный
«код»
для
конкретного
класса.
3. Template
это
…
• Шаблон
– это
параметрическая
функция
или
класс.
• Параметром
может
являться
как
значение
переменной
(как
в
обычных
функциях)
так
и
тип
данных.
• Параметры
подставляются
на
этапе
компиляции
программы.
• Подставляя
параметры
в
шаблон
– мы
конструируем
новый
тип
данных
(или
функцию,
если
это
шаблон
функции)
4. Простой
шаблон
Example50_Template
template <class T> class Print
{
public:
Print(T value)
{
std::cout << "Value:" << value <<
std::endl;
};
};
Перед
описанием
класса
ставим
ключевое
слово
template
<class
T>
T
– используем
вместо
имени
класса,
который
будет
заменяться
при
создании
конкретного
экземпляра
класса.
Print – это
шаблон
Print<int> -‐ это
класс,
сконструированный
по
шаблону
5. Несколько
параметров
и
шаблоны-‐функции
Example51_MultiTemplate
Параметры
указываются
через
запятую:
template <class A, class B> class Sum { …}
Оператор,
принимающий
в
качестве
параметра
– шаблон
с
параметрами:
template <class A, class B> std::ostream& operator<<(std::ostream & os, Sum<A, B> &sum)
6. Параметры
– переменные
Example52_ComplexParameters
1.template <class TYPE, TYPE def_value, size_t SIZE = 10 > class Array {
2.protected:
3. TYPE _array[SIZE];
4.public:
5. Array() {
6. for (int i = 0; i < SIZE; i++) {
7. _array[i] = def_value;
8. }
9. }
10. const size_t size() {
11. return SIZE;
12. }
13. const TYPE operator[](size_t index) {
14. if ((index >= 0) && (index < SIZE)) return _array[index];
15. else throw BadIndexException(index, SIZE);
16. }
17.};
7. Специализация
шаблонов
Example53_TemplateSpecialization
template <class T>
class mycontainer {
// …
};
template <>
class mycontainer <char> {
// ..
};
Иногда
бывает
необходимость
сделать
специальную
реализацию
шаблона
для
какого-‐либо
типа.
В
этом
случае,
можно
описать
отдельную
реализацию
класса,
дополнив
его
новыми
методами
или
переопределив
реализацию
существующих.
8. Можно
специализировать
только
часть
параметров
Example54_TemplateSpecialization2
template <class A, class B,
class C> class Sum {
…
}
template <class A, class B>
class Sum<A, B, const char*> {
…
}
При
частичной
специализации
у
шаблона
становится
меньше
параметров
(какие-‐то
мы
уже
указали
явно).
Частичная
специализация
работает
только
с
классами
(с
функциями
не
работает).
9. Вычисляем
факториал
Example54_Factorial
1.// факториал с помощью функций
2.template <uint64_t value> uint64_t Factorial(){
3. return Factorial<value-1>()*value;
4.}
5.template <> uint64_t Factorial<0>(){
6. return 1;
7.}
8.// факториал с помощью классов
9.template<uint64_t n>class fact{
10. public:
11. static const uint64_t value = fact<n-1>::value * n;
12.};
13. template<>class fact<0>{
14. public:
15. static const uint64_t value = 1;
16.};
10. Templates
две
модели
1.
Наиболее
популярный
подход
-‐ модель
включения(inclusion model),
определения
шаблонов
полностью
размещаются
в
заголовочном
файле.
2.
Модель
явного
инстанцирования (explicit
instantiation model),
как
правило
реализуется
директивой
явного
инстанцирования (explicit
instantiation directive).
11. Inclusion
model
template<class T> class stack {
T* v;
T* p;
int sz;
public:
stack(int s) { v = p = new T[sz=s]; }
~stack() { delete[] v; }
void push(T a) { *p++ = a; }
T pop() { return *--p; }
int size() const { return p-v; }
};
И
объявление
и
описание
шаблона
располагается
в
header
файле
(.h)
Фактически,
при
любом
подключении
к
.cpp файлу
– это
будет
новый
шаблон
для
компилятора.
Минус
такой
модели
в
том,
что
трудно
читать
код
(все
перемешано).
13. В
продолжение
примера
1. В
качестве
параметра
шаблона
можно
передавать
указатели
на
функции
(если
работать
не
с
указателями
– то
это
уже
будет
вызов
функции
J )
2. В
примере
«параметр-‐функция»
нам
понадобился
что
бы
удалять
указатели.
Если
бы
мы
в
коде
написали
«delete
old-‐>item»
то
такой
код
не
скомпилировался
бы
для
класса
MyStack<MyClass>.
3. А
вот
для
MyStack<MyClass*>
-‐ скомпилировался
бы.
14. Шаблоны
с
переменным
числом
параметров
Example56_VariadicTemplate
template <class T> void print(const T&
t) {
std::cout << t << std::endl;
}
template <class First, class... Rest>
void print(const First& first, const
Rest&... rest) {
print(rest...);
}
• В
C++
есть
возможность
сделать
шаблон
с
переменным
числом
параметров.
• В
этом
случае
используется
«…»
для
указания
списка
параметров.
• Работать
с
такими
шаблонами
можно
по
принципу
рекурсии.
15. Variadic template
в
структурах
данных
Example57_VariadicTemplate2
1.// Конец рекурсии
2.template <class... Ts> class tuple {};
3.// Шаблон
4.template <class T, class... Ts>
5.// Класс наследник самого себя но с меньшим числом параметров
6.class tuple<T, Ts...> : public tuple<Ts...> {
7. public:
8. tuple(T t, Ts... ts) : tuple<Ts...>(ts...), value(t) {}
9.// Ссылка на родителя
10. tuple<Ts...> &next = static_cast<tuple<Ts...>&>(*this);
11.// Последний параметр
12. T value;
13.};
18. Вспомогательный
тип
1.// специальная структура для определения типа конкретного элемента в
tuple
2.template <size_t, class> struct elem_type_holder;
3.// без параметра - это тип базового класса
4.template <class T, class... Ts> struct elem_type_holder<0, tuple<T,
Ts...>> {
5. typedef T type; // тип
6.};
7.// это тип k-го класса в цепочке наследования
8.template <size_t k, class T, class... Ts> struct elem_type_holder<k,
tuple<T, Ts...>> {
9.typedef typename elem_type_holder<k - 1, tuple<Ts...>>::type type;
10.};
19. Шаблонная
функция
get
Example57_VariadicTemplate2Full
1.// шаблон функции get для получения параметра (данная специализация работает только при
index==0)
2.template <size_t index,class ...Ts>
3.typename std::enable_if<index == 0,
4. typename elem_type_holder<0, tuple<Ts...>>::type&>::type
5.get(tuple<Ts...>& t){
6. return t.value;
7.}
8.// шаблон функции get для получения параметра (данная специализация работает только при
index!=0)
9.template <size_t index,class T,class ...Ts>
10.typename std::enable_if<index != 0,
11. typename elem_type_holder<index, tuple<T,Ts...>>::type&>::type
12.get(tuple<T,Ts...>& t){
13. tuple<Ts...> &base = t.next;
14. return get<index-1>(base);
15.}
20. CRTP
(Curiously
Recurring
Template
Pattern)
Example60_CRTP
template <class T>
class base{};
class derived : public
base<derived> {};
Такая
конструкция
делает
возможным
обращение
к
производному
классу
из
базового!
22. Шаблоны
в
качестве
параметров
шаблонов
Example59_TemplateParameter
• Шаблон
можно
указать
в
качестве
параметра
шаблона!
• Все
типы,
которые
используются
при
конструировании
нового
типа
с
помощью
шаблона
– должны
быть
его
параметрами.
template <class T> class Payload
{
…
};
template <template <class> class
PL, class T> class Printer
{
…
};
Printer<Payload, int> printer;
23. Что
нового
в
C++:
auto
Example62_Auto
В
С++11 auto позволяет
не
указывать
тип
переменной
явно,
говоря
компилятору,
чтобы
он
сам
определил
фактический
тип
переменной,
на
основе
типа
инициализируемого
значения.
Это
может
использоваться
при
объявлении
переменных
в
различных
областях
видимости,
как,
например,
пространство
имен,
блоки,
инициализация
в
цикле
и
т.п.
auto
i =
42;
//
i -‐ int
auto
l
=
42LL;
//
l
-‐ long
long
auto
p
=
new
foo();
//
p
-‐ foo*
Использование auto позволяет
сократить
код
(если,
конечно,
тип
не int,
который
на
одну
букву
меньше).
Не
может
использоваться
в
объявлении
параметров
функции
или
класса;
24. Протечка
абстракции
Неудобной
составляющей
работы
с
коллекциями
объектов
родительского
типа
является
необходимость
приведения
родительского
типа
к
типу-‐наследнику
(для
выполнения
необходимых
операций
над
элементом
коллекции).
Т.е.
мы
жертвуем
статическим
контролем
типов.
25. Протеска
абстракции
номер
1
Example66_AbstractionLeak1
1. class Figure{
2. public:
3. virtual double Square()=0;};
4. class Circle : public Figure{
5. public:
6. double Square() override{
7. return 3.14*3.14*R;};};
8. class Sphere : public Circle{
9. public:
10. double Volume() {
11. return 3.14*3.14*3.14*R; };
12. };
// abstraction leak
for(Figure *figure:array)
{
Sphere *sphere = dynamic_cast<Sphere*> (figure);
if(sphere!=nullptr)
std::cout << "Volume:"
<< sphere->Volume()
<< std::endl;
}