2. Шаблоны: прагматический подход В C++ шаблоны возникли из практических потребностей разработчиков библиотек. Шаблоны классов: Обобщенные контейнеры. Обобщенные потоки. Шаблоны функций: Замена стандартных макросов ANSI C Операции над обобщенными контейнерами и потоками.
3. Если бы не шаблоны классов... Указатели на «что-то» (void*) отказ от статической типизации. Указатели на искусственный базовый класс неоправданное усложнение иерархии, злоупотребление наследованием. class Stack { void push(void*); void* pop(); }; void f() { Stack s; s.push((void*) new Cat); Dog* dog = (Dog*) s.pop(); dog->bark(); }; class StackElem {/*???*/}; class Stack { void push(StackElem*); StackElem* pop(); }; class Cat : public StackElem {}; class Dog : public StackElem {};
4. Если бы не шаблоны функций... Макросы отказ от статической типизации, ошибки кодирования , неожиданности при вычислении . Перегрузка утомительное и чреватое ошибками копирование кода. Указатели на искуственный базовый класс см. выше. #define max(a,b) ((a>b) ? (a) : (b)) const Comparable& max(const Comparable&, const Comparable&); int max(int a, int b) {return (a>b) ? a : b;} short max(short a, short b) {return (a>b) ? a : b;} long max(long a, long b) {return (a>b) ? a : b;} float max(float a, float b) {return (a>b) ? a : b;} double max(double a, double b) {return (a>b) ? a : b;}
5. Когда использовать шаблоны - самый простой критерий Перечисленные выше примеры «кривого кода» - типичные индикаторы того, что пора применить шаблоны. Повторения одного и того же кода, отличающиеся только именами типов. Искуственные базовые классы, не предоставляющие никакого содержательного интерфейса. Сложные параметризованные макросы (не содержащие специальных лексических операций типа склейки или преобразования в строку).
6. Параметры шаблонов Non-type parameters ( template<int N> ): на практике почти не употребляются (например, в стандартной библиотеке - ни одного). Type parameters (template<class T>): подставляемый тип может быть любым, но делаются некоторые предположения о его интерфейсе («должна быть функция f , которую можно вот так вот вызвать»). Важный момент : интерфейс, требуемый шаблоном, реализовывается без применения наследования («у нас просто есть функция f , мы ее не наследуем и не переопределяем »).
7. Instantiation Отношение классификатор-объект a.k.a. class-instance: class C {…}; C x; Объект x - экземпляр класса C (instance). template<class T> class C {…}; C<D> x; Класс C<D> - экземпляр шаблона C (instantiated class). Объект x - экземпляр класса C<D> (instance). template<class T> void f(T) {…} … f<int>(0); Функция f<int> - экземпляр шаблона f (instantiated function). Действие компилятора по созданию экземпляра шаблона - instantiation ( инстанциация? инстанцирование ? создание экземпляра ? ).
8. Шаблон как область действия Шаблон класса не есть класс! Следовательно, шаблон класса не есть область действия ( scope ). template<class T> class C {static void g();}; Нет никакой C::g! Есть C<int>::g, C<std::string>::g и т.д. Как сделать шаблон областью действия (т.е. создавать функции/данные, относящиеся ко всем экземплярам шаблона)? Унаследовать шаблон от базового класса, не являющегося шаблоном. class B {static void g();}; template<class T> class C : … B {…}; Какое наследование ( public/private, virtual etc. ) применить? Зависит от моделируемого отношения ( “is-a” etc. ) и интерфейса (public etc.), к которому относится имя (C::g).
9. Specialization Отдельная реализация шаблона при конкретных значениях одного или нескольких параметров. Результат - класс или функция (если зафиксированы все параметры) или шаблон с меньшим числом параметров. Применения: Оптимизированные версии. Информационные классы (traits) . template<class T> void MyCopy(T* dst, T* src, int n) { if(dst && src && (dst != src) for(int i = 0; i < n; i++) dst[i] = src[i]; } template<> void MyCopy<int>(int* dst, int* src, int n) {memmove(dst, src, n);}
10. Реализация шаблонов В первом приближении, шаблоны - это макросы с проверкой типов. template<class T> class Stack {public: Stack() {} void push(T) {…} T pop() {…} }; void f() { Stack<int> is; Stack<string> ss; } class Stack_int {public: Stack_int() {} void push(int) {…} int pop() {…} }; class Stack_string {…}; void f() { Stack_int } А что во втором приближении ? Проблема «code bloating». Template instantiation
11. Понятие связывания ( linkage) Файл (translation unit) C++ есть последовательность объявлений ( declarations ). Каждое объявление вводит одно или несколько имен ( names ). Имя может иметь внешнее или внутреннее связывание ( external / internal linkage). Внешнее связывание имя видно из других файлов. Примеры: глобальные функции; классы. Внутреннее связывание имя не видно из других файлов. Примеры: имена , объявленные как static, inline или const. Физический смысл: имена с внешним связыванием видны компоновщику через заголовок объектного файла . Имя с внешним связыванием должно быть уникально в программе ( иначе - ошибка компоновки ).
12. Какое связывание имеет экземпляр шаблона ? Файлы компилируются независимо. Какое связывание имеет foo<X> в file1.cpp и file2.cpp? Внешнее нельзя, т.к. будут дублирующиеся имена. Внутреннее можно, но тогда копии кода будут содержаться в нескольких файлах (« code bloating », «раздувание кода»). Стандарт молчит, имплементации выкручиваются. Основной способ - нестандартные компоновщик, формат объектного файла и специальный вид связывания. Та же проблема с inline virtual методами. // foo.h template<class T> void foo(T* p) {p->xyz();} // file1.cpp #include “foo.h” void bar1(X* x) {foo(x);} // file2.cpp #include “foo.h” void bar2(X* x) {foo(x);}
13. Generic programming Немолодая идея (младше ATD , старше OOP ). В оригинальном виде: максимально полное разделение структур данных и обрабатывающих их алгоритмов. “ Programming with concepts” A concept is a family of abstractions that are all related by a common set of requirements, where the requirements consist of valid expressions, associated types, invariants and complexity guarantees. В строго типизированных языках ( Ada, C++ ) приводит к механизму параметризованных типов . В безтиповых и близких к ним ( Smalltalk, скриптовые языки) получается само собой.
14. П аттерны (1) Существующие общие определения паттерна: A description of communicating objects and classes that are customized to solve a general problem in a particular context. (GoF) A three-part rule, which expresses a relation between a certain context, a problem, and a solution. (Alexander) An especially clever way of solving an entire particular class of problems. (Eckel) Более узкое определение паттерна с точки зрения современного OOD: Паттерн есть параметризованная кооперация.
15. П аттерны (2) Два основных определения: Сommunicating objects that are customized to solve a general problem in a particular context. П араметризованная кооперация. Как соотносятся эти два определения? «Кооперация» « communicating objects », « general problem ». «Параметризованная» « customized », « particular context ». Какова связь с параметризованными типами и generic programming? Паттерн реализуется в виде набора взаимосвязанных шаблонов. Паттерн используется путем подстановки в шаблоны классов, используемых в конкретном контексте.
#15:Alexander: Each pattern is a three-part rule, which expresses a relation between a certain context, a problem, and a solution. As an element in the world, each pattern is a relationship between a certain context, a certain system of forces which occurs repeatedly in that context, and a certain spatial configuration which allows these forces to resolve themselves. As an element of language, a pattern is an instruction, which shows how this spatial configuration can be used, over and over again, to resolve the given system of forces, wherever the context makes it relevant.
#16:Alexander: Each pattern is a three-part rule, which expresses a relation between a certain context, a problem, and a solution. As an element in the world, each pattern is a relationship between a certain context, a certain system of forces which occurs repeatedly in that context, and a certain spatial configuration which allows these forces to resolve themselves. As an element of language, a pattern is an instruction, which shows how this spatial configuration can be used, over and over again, to resolve the given system of forces, wherever the context makes it relevant.