
#9 Шаблоны.pptx
- Количество слайдов: 24
Шаблоны Степанюк Константин Сергеевич 24 const@gmail. com
Задание • Описать чисто абстрактные классы Can. Fly и Can. Speak, имеющие методы fly() и speak() соответственно. • Реализовать класс Parrot (попугай), имеющий поле name и наследующий классы Can. Fly и Can. Speak – Методы fly() и speak() выводят на экран сообщение – Для поля name реализовать селектор и модификатор.
Первопричины • Основная первопричина –желание параметризовать контейнерные классы (списки, динамические массивы, множества, ассоциативные массивы и т. д. ), а также общие для многих типов функции • До появления шаблонов очень интенсивно использовался препроцессор для целей автоматической генерации кода классов и функций • Альтернативой использованию препроцессора при отсутствии шаблонов является подход, основанный на наследовании с общим корнем, при котором в качестве хранимых объектов контейнера могут использоваться объектыклассов-наследников определенного базового типа (например Object)
Использование средств препроцессора /* файл stack. h */ class ELEM_stack { ELEM *min, *top, *max; public: void push (ELEM); ELEM pop (void); }; #define ELEM long #define ELEM_stack long_stack #include “stack. h” #undef ELEM_stack class long_stack ls(1024);
Недостатки использования препроцессора • Генерация кода «шаблонного» класса или функции происходит до процесса синтаксического анализа, и таким образом, не используется статическая проверка типов на стадии «инстанцирования» кода «шаблона» • Код «шаблонного» класса всегда генерируется полностью в не зависимости от того, какие члены используются а какие –нет. Всегда происходит явное инстанцирование такого «шаблона» • Отсутствует возможность специализации шаблонов • Препроцессор С плохо совместим с основными концепциями и идеями С++ и его чрезмерное использование как правило приводит к трудно поддерживаемому и развиваемому коду
Использование системы с общим корнем class Stack { Object *min, *top, *max; public: void push (Object*); Object* pop (void); }; class My. Class : public Object { }; class Another. Class: public Object {}; My. Class x; Another. Class y; Stack s; s. push(&x); s. push(&y); Another. Class *p 1 = (Another. Class*) s. pop(); My. Class *p 2 = (My. Class*) s. pop();
Недостатки подхода с общем корнем • Необходимость в явном приведении типа при извлечении объектов из контейнера • Трудность защиты от использования разнородных объектов внутри контейнера • Ограниченность функционала по манипуляции с объектами внутри «обобщенного» класса исключительно контрактом предусмотренном в базовом классе (в примере – Object) • В С++ отсутствует выделенный базовый для всех типов корневой класс (в Smalltalk, Java и. NET –class Object) • Отсутствие возможности работы в контейнерах со значениями встроенных типов • Высокие накладные расходы во время исполнения
Шаблоны классов C++ - основная идея • Использование «родственных» классов, имеющих общую структуру и поведение, но оперирующих разными типами – параметрами: template<class T> class vector { … }; vector<int> vint; vector<double> vdouble; vector<string> vstring; vector< vector <short>> vshort; cout<<vint. size()<<endl; vdouble[5] = 5. 0; vstring. push_back( string (“hello”) ); • При этом сами типы являются независимыми: vint = vstring; //Ошибка –разные типы!!!
Терминология • Шаблон(template) –это механизм, позволяющий использовать типы как параметры класса или функции. Получив эти аргументы компилятор генерирует конкретный класс или функцию • Часто шаблонный класс называют порождающим типом(type generator)или родовым типом. Процесс генерирования типов (классов) с помощью шаблонного класса по заданным аргументам шаблона называется конкретизацией шаблона(template instantiation) – инстанцированием • Иногда для процесса «инстанцирования» применяют термин «специализация» (specialization), что может вызвать терминолгическую путаницу с понятием специализации шаблонов как части параметрического полиморфизма (рассмотрим позже)
Обобщенное программирование • Обобщенное программирование – это создание кода, работающего с разными типами, заданными в качестве аргументов, причем на данные типы могут накладываться специфические синтаксические и семантические требования • шаблонный класс (class template) – параметризованный тип, или параметризованный класс (parameterized type) • шаблонная функция (function template) –параметризованная функция (parameterized function), также часто называемая алгоритмом (algorithm) • В этом смысле обобщенное программирование иногда называют алгоритмически ориентированным программированием (algorithm-oriented programming) при котором акцент проектирования переносится на параметризуемые алгоритмы, а не на сами типы
Полиморфизм - итог • Polymorphus – «многообразный» , «много форм» , «множественное строение» - одно «имя» , но несколько разных «тел» • Параметрический полиморфизм (parametric polymorphism): – перегрузка функций - выбор «тела» на основе типов аргументов – инстанцирование шаблонов (генерация «тела» ) по параметрам – типам – происходит на стадии компиляции • Виртуальный полиморфизм, он же «специальный» полиморфизм – выбор реализации ( «тела» ) виртуальной функции во время исполнения на основе реального типа объекта
Сочетание обобщенного и объектноориентированного стилей //Обобщенный тип vector //генерация vector<Shape*> при компиляции void draw_all(const vector<Shape*> &vpshapes) { //Шаблон vector дает нам size() и operator[] for(int i = 0; i < vpshapes. size( ); ++i ) { //Shape дает нам virtual void draw() //вызов через таблицу виртуальных функций vpshapes[ i ] -> draw() ; } }
Преимущества шаблонов C++ • Хорошо совместимы с концепцией строгой типизации и проверки совместимости типов на стадии инстанцирования кода и компиляции «обобщенных» классов и функций • На столько же эффективны в плане производительности как и «жестко заданные» обычные типы, в качестве параметров шаблонов при инстанцировании можно использовать встроенные типы • Скорость компиляции и работы системы разработки (среды) при использовании шаблонов на столько же высока как и при использовании обычных «жестко заданных» типов • Поддержка специализации (уточнения) и наследования • Совместно с механизмом исключений и пространствами имен делают С++ очень мощным ОО инструментом для написания сложных программных систем
Общий синтаксис объявления и определения template-declaration: exportopt template <template-parameters-list> declaration template-parameters-list: template-parameters-list, template-parameter declaration: -определение или объявление функции или класса -определение функции-члена, класса-члена, или статического члена данных шаблона класса либо класса вложенного в шаблон -определение шаблона члена класса либо шаблона класса
Параметры шаблона template-parameter: type-parameter-declaration type-parameter: class identifieropt = type-id typename identifieropt = type-id template <template-parameters-list> class identifieropt = id-expression
Параметры шаблона • Параметры шаблона не являющиеся типами (a non-type template parameter), могут: – иметь целый или перечислимый тип – быть указателем на функцию или объект – быть ссылкой на функцию или объект • Параметры не являющиеся типами не могут иметь тип вещественного (floating point) значения, класса или быть типа void template <double d> class. X; //error! template <double *d> class Y; //ok! template <My. Class o> class Z; //error! template <My. Class &r> class ZZ; //ok!
Пример –шаблон строки template < class C = char > class String { struct Srep; Srep* rep; public: String(); String(const String&); String(const C*); C read(int i) const; const String& operator=(const C*); }; template < class C > struct String< C >: : Srep { C* s; int size; int n_ref; }; template < class C > C String<C>: : read(int i) const { return rep->s[i]; }
Использование шаблонов String<> cs; //Строка обычных символов (аргумент по умолчанию) class Jchar {…}; // Японские символы String < Jchar > js; //Японская строка String <unsigned char> us; //Строка беззнаковых символов int main (int argc, char** argv) { String<> buf; map< Stirng<char>, int > m; while(cin>>buf) m[buf]++; //Подсчет количества повторений введенных слов //… }
Использование параметров «не типов» template <class T, int i= 16> class Buffer { T v[i]; //i является константой внутри шаблона int sz; public: Buffer(): sz(i) {} //… }; Buffer <char> small_cbuf; Buffer <char, 127> cbuf; Buffer <char, i> ibuf; //error! Необходима константа! Buffer <Record, 8> rbuf; // Для каждого из объектов small_cbuf, rbuf // генерируется отдельный класс • Если имя типа очень длинно, то часто используют typedef
Процесс инстанцирования шаблона • Процесс генерации класса (или функции) по шаблону класса (или функции) и аргументам шаблона называется инстанцированием шаблона (template instantiation) • Какой код и когда сгенерировать определяет компилятор String <char> cs; void f() { String<Jchar> js; cs = “Какой код сгенерировать решает компилятор”; } //В данном случае будут сгенерированы: // Объявления для String<char>, String<Jchar> // String<char>: : Srep, String<Jchar>: : Srep // Код для их конструкторов по умолчанию и деструкторов // Код для String<char>: : operator=(char*)
Эквивалентность типов • При наличии шаблона мы генерируем типы путем задания аргументов шаблона • При использовании одного и то го же набора аргументов шаблона мы всегда получаем один и тот же сгенерированный тип String<char> s 1; String<unsigned char> s 2; String<int> s 3; typedef unsigned char Uchar; String<Uchar> s 4; String<char> s 5; Buffer<String<char>, 10> b 1; Buffer<char, 10> b 2; Buffer<char, 20 -10> b 3; //В данном случае типы s 2 и s 4, s 1 и s 5, b 2 и b 3 совпадают.
Проверка типов и нахождение ошибок • При определении шаблона компилятор пытается обнаружить синтаксические и, возможно, другие ошибки, которые можно обнаружить вне зависимости от конкретных аргументов шаблона template <class T> class List { struct Link { Link *pre, *suc; T val; Link(Link *p, Link *s, const T& v): pre(p), suc(c), val(v) {} } //Ошибка отсутствует ; Link *head; public: List(): head(7) {} //Ошибка –инициализация указателя целым List(const T& t): head(new Link(0, o, t)) {} //Ошибка – o void print_all() { for (Link *p = head; p; p=p->suc) cout <<p->val <<endl; } };
Проверка типов (продолж. ) class Rec {/*…*/}; void f (List<int> &li, List<Rec>& lr) { li. print_all(); //ok! lr. print_all(); //error! Ошибка связывания –для Rec // не определен оператор ostream& operator<< }
Задание • Реализовать шаблон функции bubble. Sort для сортировки чисел. • Описать и реализовать шаблон класса Stack, c с методами pop() и push(const T&).
#9 Шаблоны.pptx