Языки программирования, 28 лекция (от 14 декабря)
Материал из eSyr's wiki.
(Содержимое страницы заменено на «== From Ebaums Inc to MurkLoar. == We at EbaumsWorld consider you as disgrace of human race. Your faggotry level exceeded any imaginab...») |
(Отмена правки № 1275 участника 88.134.92.10 (обсуждение)) |
||
Строка 1: | Строка 1: | ||
- | == | + | ''С++''<br> |
- | + | ''Ада''<br> | |
- | + | ''С#'' c 2004<br> | |
- | + | ''Java'' c 2005<br> | |
+ | |||
+ | Все современные языки, использующиеся в промышленности, поддерживают идею статической параметризации. | ||
+ | Продолжаем рассматривать идею родовых модулей в ''Аде''. | ||
+ | |||
+ | '''п.1. Родовые модули языки ''Ада'' ''' | ||
+ | |||
+ | ''Ада-83'' – принцип РОРИ пронизывает весь этот язык. Получился очень мощный и одновременно эффективный механизм. Порождение нового пакета порождало новый экземпляр данного типа. | ||
+ | |||
+ | package Stack – два параметра – тип элемента размер стека: | ||
+ | |||
+ | <pre> | ||
+ | generic | ||
+ | type T is private; | ||
+ | size: integer; | ||
+ | package Stack is | ||
+ | Push | ||
+ | Pop | ||
+ | end Stack; | ||
+ | </pre> | ||
+ | |||
+ | Можно ли так запрограммировать процедуры, чтобы они эффективно работали для любого размера стека? Конечно, да! | ||
+ | |||
+ | Конкретизация: | ||
+ | |||
+ | <pre> | ||
+ | package IStack is new Stack(integer, 128); | ||
+ | </pre> | ||
+ | |||
+ | Тривиальная реализация – простая макроподстановка integer и 128. Плюс – крайняя простота. Минус – очень сильное разбухание кода – сколько объявлений, столько и различных процедур. ''Ада-83'' требовала отдельной компиляции спецификации и реализации. | ||
+ | |||
+ | Теперь представим себе другую ситуацию, например пусть у нас есть родовая процедура сортировки: | ||
+ | |||
+ | <pre> | ||
+ | generic | ||
+ | type ElType is private; | ||
+ | type Index is range <>; | ||
+ | type Arr is array (Index) of ElType; | ||
+ | with function <(x, y : T) return Boolean (<>); | ||
+ | procedure G_SORT(A: in out Arr; L,R : Index); | ||
+ | </pre> | ||
+ | |||
+ | Как написать спецификацию той же шаблонной функции на ''С++'', считая, что уже имеется Vector <T>? Вот так: | ||
+ | |||
+ | <pre> | ||
+ | template <class T, class Index> | ||
+ | void Sort(Vector <T>&V, Index L,R); | ||
+ | </pre> | ||
+ | |||
+ | В процедуре Sort обязательно встретится нечто вроде: | ||
+ | |||
+ | <pre> | ||
+ | if (v[i]<v[j]) | ||
+ | </pre> | ||
+ | |||
+ | Только во время конкретизации происходит процесс выведения типа и компилятор узнает, есть ли вообще у v операция индексирования или нет. До этого производится только синтаксическая проверка. | ||
+ | |||
+ | В ''Аде'' большое разнообразие типов формальных параметров родового модуля. | ||
+ | Формальные параметры родовых модулей: | ||
+ | |||
+ | параметр-переменная <=> любое константное выражение<br> | ||
+ | type T is private <=> любой тип с операцией присваивания<br> | ||
+ | type T is range <=> любой дискретный тип с упорядоченностью и функциями «следующий» и «предыдущий»<br> | ||
+ | type T is delta <=> любое плавающее выражение<br> | ||
+ | < > <=> при конкретизации процедуры мы можем не указывать этот вариант (параметр по умолчанию).<br> | ||
+ | |||
+ | Пусть у нас есть: | ||
+ | |||
+ | <pre> | ||
+ | procedure StrSort is new G_SORT(String, Integer, TARR); | ||
+ | </pre> | ||
+ | |||
+ | C помощью < > компилятор находит функцию < для строк и подставляет как параметр по умолчанию. | ||
+ | Родовые сегменты – абсолютно необходимая в ''Аде'' конструкция, потому что там нет передачи процедур и функций по параметру. Как по-другому реализовать функцию, скажем, вычисляющую интеграл? Мы должны передавать вещественный тип-параметр и саму подынтегральную функцию. Допустим, мы вводим новый вид чисел, например комплексный, то есть запись. Мы должны описать тип чисел как приватный и передавать как сравнение, так и всю арифметику. Хотя много всего, но зато будет одна процедура и для вещественных, и для комплексных чисел, и для кватернионов. | ||
+ | Компилятор должен видеть не только спецификацию данной абстракции, но и тело. Гибкость повышается, но тело родовой абстракции должно быть доступно в любой момент конкретизации. За в этой жизни надо платить! Либо мы повышаем гибкость, либо производительность. В ''Аде-83'' механизм родовых модулей – единственный, который поддерживал ОО. Как вы думаете, что самое главное, что появилось в ''Аде-95''? Правильно, класс – тегированная запись: | ||
+ | |||
+ | <pre> | ||
+ | type T is tagged | ||
+ | </pre> | ||
+ | |||
+ | Какова была основная цель введения дженериков в ''C#'' и ''Java''? Для коллекций. Если мы почитаем определение языка ''C#'', то первый шаблон, который мы найдём, это реализация стека: | ||
+ | |||
+ | <pre> | ||
+ | Elem Stack {} | ||
+ | </pre> | ||
+ | |||
+ | Раньше же мы видели Object []body; | ||
+ | Дальше приведение типа, и если мы пропускаем проверку, получим ошибку времени выполнения. В основном коллекции являются не гетерогенными, и, используя шаблоны, можно сильно увеличить производительность. Самый мощный механизм – шаблоны в ''С++''. Почему же они более мощны и гораздо более сложны, чем в других языках? | ||
+ | |||
+ | Шаблоны в ''С++'': <br> | ||
+ | 1) Функции<br> | ||
+ | 2) Классы<br> | ||
+ | <br> | ||
+ | а) Параметр-класс <=> Любой тип<br> | ||
+ | б) Параметр-переменная <=> Константное выражение<br> | ||
+ | |||
+ | <pre> | ||
+ | template <class T, int size> class Stack {...} | ||
+ | </pre> | ||
+ | |||
+ | T – тип данных, size может быть везде, где фигурирует целая константа. Конкретизация происходит явным образом. | ||
+ | Stack <int, 256> может появляться везде, где может появляться имя типа. | ||
+ | После Stack <int, 256>, увидев Stack <int, 2*128>, компилятор ничего не сгенерирует, т.к. уже один раз сгенерировал. | ||
+ | А вот Stack <int, 64> порождает новый класс, хотя в общем-то Push и Pop в этом случае делают одно и то же, что и в случае Stack <int, 256>. Но компилятор мужественно породит новый кусок кода… | ||
+ | |||
+ | Stack <int, 64> S; | ||
+ | Вот здесь начинается генерация кода. Но в ''С++'' у нас раздельная трансляция. | ||
+ | Шаблоны – очень мощная вещь, но неграмотное их использование неопытным программистом ведет к разбуханию кода… | ||
+ | Если одинаковые шаблоны сгенерированы в разных модулях, некоторые компиляторы борются с этим, используя PCH, хотя это делает трансляцию зависимой. | ||
+ | |||
+ | Для функций то же самое, но конкретизация не обязательна. Рассмотрим: | ||
+ | |||
+ | <pre> | ||
+ | template <class T> void Sort (Vector <T>&V); | ||
+ | </pre> | ||
+ | |||
+ | Sort порождает бесконечное семейство конкретных реализаций. | ||
+ | |||
+ | Представим, что у нас в программе есть: | ||
+ | |||
+ | <pre> | ||
+ | Vector <string> X; | ||
+ | Sort(X); | ||
+ | </pre> | ||
+ | |||
+ | В Sort должен явно встречаться код: | ||
+ | |||
+ | <pre> | ||
+ | if (v[i]<v[j]) | ||
+ | </pre> | ||
+ | |||
+ | Компилятор опять же ничего проверить не может. Эта проверка происходит только в момент конкретизации. В ''Аде-95'' и ''С++'' ситуация одинаковая и не такая, как в ''Аде-83''. В момент конкретизации компилятору надо дать полную информацию обо всём шаблоне, и ни о каком принципе РОРИ речи идти не может. | ||
+ | Этим ''С++'' не ограничивается. Пока что было очень похоже на ''Аду-95''. Какой недостаток? | ||
+ | |||
+ | В первых версиях шаблонов в ''С++'' было невозможно: | ||
+ | |||
+ | <pre> | ||
+ | template <class T> | ||
+ | T f() {…} | ||
+ | </pre> | ||
+ | |||
+ | В новых версиях можно: | ||
+ | |||
+ | <pre> | ||
+ | Sort <int>(a); | ||
+ | </pre> | ||
+ | |||
+ | Но это не всё. В ''С++'' есть механизм частичной специализации. | ||
+ | |||
+ | '''п.2. Шаблоны в ''С++''''' | ||
+ | |||
+ | <pre> | ||
+ | Vector <const char *>a; | ||
+ | Sort(a); | ||
+ | </pre> | ||
+ | |||
+ | Будет подставлена функция < для указателей, и ошибки в программе не будет, но работать она будет неправильно. | ||
+ | |||
+ | <pre> | ||
+ | template <class T> bool less (T& x, T& y) { | ||
+ | return x<y; | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | Это описание шаблонной функции. Хорошая функция сортировки должна работать через шаблонную функцию сравнения. Но проблема со * остаётся. Что делать? А вот что. | ||
+ | |||
+ | Пишем: | ||
+ | |||
+ | <pre> | ||
+ | template <> bool less <const char *> (const char *p1, const char *p2) { | ||
+ | return strcmp(p1,p2); | ||
+ | } | ||
+ | |||
+ | template <> class Vector <void *>; | ||
+ | </pre> | ||
+ | |||
+ | Подставь вместе T void * и сгенерируй код. (?) | ||
+ | |||
+ | Для векторов возможны такие реализации: | ||
+ | |||
+ | 1) template <class T> class Vector {…}<br> | ||
+ | 2) template <> class Vector <T*>;</br> | ||
+ | |||
+ | <pre> | ||
+ | Vector <long > V; | ||
+ | Vector <int> V1; | ||
+ | </pre> | ||
+ | |||
+ | Оба по первой реализации и разные. | ||
+ | |||
+ | <pre> | ||
+ | Vector <const char *> v2; | ||
+ | Vector <int *>v3; | ||
+ | </pre> | ||
+ | |||
+ | Оба по второй и одинаковые. | ||
+ | |||
+ | Будет также эффективно, как и встроенный в язык вектор. | ||
+ | Механизм частичной специализации очень эффективен. | ||
+ | Физики очень не любили использовать язык ''С''. Компиляторы с ''ФОРТРАНА'' были, как ни странно, эффективнее компиляторов с ''С''. ''С''-шные функции требуют слишком много проверок, пример: memcpy (проверяет перекрытие областей памяти и много-много чего прочего). | ||
+ | С появлением шаблонов ситуация изменилась. Многие математические библиотеки, такие как Boost, Loki – это именно библиотеки шаблонов – очень производительны. Читай у Александреску про его библиотеку шаблонов Loki. У STL много недостатков, но написана она исключительно производительно в смысле эффективности. | ||
+ | |||
+ | Вспомним: | ||
+ | |||
+ | <pre> | ||
+ | Find(It first, It last, …) | ||
+ | </pre> | ||
+ | |||
+ | Для каждого конкретного типа можно написать частичную специализацию Find | ||
+ | *I == X (?) | ||
+ | |||
+ | Можно передавать третьим параметром compare, и вызывать но это неэффективно, так как это срывает работу конвейера. Выход – функтор. Перекрываем операцию ( ): | ||
+ | |||
+ | <pre> | ||
+ | сlass Finder { | ||
+ | public: | ||
+ | bool operator ()(T&x, …) { | ||
+ | … | ||
+ | } | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | '''п.3. ''C#'', ''Java'' – обобщенные классы''' | ||
+ | |||
+ | У них очень простой синтаксис и похожая идея: | ||
+ | |||
+ | <pre> | ||
+ | class Stack <T> {…} | ||
+ | interface IComparable <T> {…} | ||
+ | </pre> | ||
+ | |||
+ | Параметризовать можно как классы, так и интерфейсы и методы: | ||
+ | |||
+ | <pre> | ||
+ | public void add <T> (T x) {…} | ||
+ | </pre> | ||
+ | |||
+ | Слово class просто синтаксически не нужно. Компилятор производит первичный синтаксический анализ и перевод в MIL/Байт-код. Конкретизация же происходит при выполнении кода. Код генерируется оптимальным образом. | ||
+ | |||
+ | В ''Java'' в качестве параметризованных типов только классы. | ||
+ | Вместо Stack <int> S надо Stack <Integer> S | ||
+ | |||
+ | В ''C#'' на такое пойтить не могли. Там же есть структуры! | ||
+ | Если аргумент – класс или интерфейс, код разный. Для ссылок одинаковый. | ||
+ | |||
+ | В ''Java'': | ||
+ | if (Stack<Integer>.getClass==Stack<String>.getClass()) даст true | ||
+ | |||
+ | В ''C#'': | ||
+ | |||
+ | <pre> | ||
+ | сlass Dictionary <K, V> { | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | K – тип ключа, | ||
+ | V – тип значения. | ||
+ | |||
+ | Явно при реализации должно быть нечто вроде: | ||
+ | |||
+ | <pre> | ||
+ | public void Add( K Key, V Value) { | ||
+ | … | ||
+ | if (K<K1) … | ||
+ | … | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | На это будет выдана ошибка. Потому что функции < может не быть. | ||
+ | |||
+ | Надо: | ||
+ | |||
+ | <pre> | ||
+ | if ((IComparable)K<K1) | ||
+ | </pre> | ||
+ | |||
+ | Вместо этого можно: | ||
+ | |||
+ | <pre> | ||
+ | class Dictionary <K, V> where K:Icomparable | ||
+ | </pre> | ||
+ | |||
+ | Можно теперь ошибки не бояться. | ||
+ | Возвращаемся к старой доброй ''Аде-83''. | ||
+ | |||
+ | «Я когда-то за счет изменения нескольких функций выиграл в производительности 30%. Это, извините меня, о-го-го!» | ||
+ | |||
+ | Кстати, по поводу ''С++''. В ''С++'', в отличие от ''Ады'', есть механизм подстановки параметров по умолчанию. | ||
+ | new | ||
+ | delete | ||
+ | |||
+ | В итоге концепция шаблонов пронизывает все современные языки. | ||
+ | Наиболее мощная концепция в ''С++'', но за счёт сложности можно писать очень неэффективные программы. | ||
+ | В ''Java'' и ''C#'' достигнут между производительностью и гибкостью. | ||
+ | На этом закончим лекцию и вместе с ней весь курс. | ||
+ | |||
+ | '''Схема проведения экзамена по Языкам Программирования:''' | ||
+ | |||
+ | 1) Экзамен письменный, длится одну пару | ||
+ | |||
+ | 2) Можно пользоваться печатными материалами, если это не типографская продукция | ||
+ | |||
+ | 3) Всего восемь задач, максимум за каждую - 6 баллов | ||
+ | |||
+ | 4) when баллы>=36 => 5, when 24<=баллы<36 => 4, when 12<=M<25 => 3, default => 2 | ||
+ | |||
+ | '''Задачи досрочного экзамена (19 декабря):''' | ||
+ | |||
+ | '''1-ый вариант.''' | ||
+ | |||
+ | 1) В каких из перечисленных ниже языков программирования при обработке | ||
+ | исключительных ситуаций используется семантика завершения? Объяснить, что она означает. | ||
+ | |||
+ | ''Ада'', ''Модула-2'', ''Оберон'', ''Оберон-2'', ''С'', ''С++'', Java, ''C#'' | ||
+ | |||
+ | 2) Объяснить, что означает оператор foreach языка ''C#''. Какие условия накладываются | ||
+ | на класс-коллекцию, чтобы он мог использоваться в данном операторе? | ||
+ | |||
+ | 3) Объяснить все смыслы ключевого слова final в языке ''Java''. | ||
+ | |||
+ | 4) Перечислите способы передачи параметров в языках программирования. Какие из них | ||
+ | реализованы в языках ''Ада-95'' и ''Оберон''? | ||
+ | |||
+ | 5) Что будет выдано в стандартный канал вывода при вызове функции F()? | ||
+ | |||
+ | <pre> | ||
+ | class X { | ||
+ | public: | ||
+ | void g() { cout << 1 << ' '; } | ||
+ | virtual void f() { g(); } | ||
+ | }; | ||
+ | |||
+ | class Y: public X { | ||
+ | public: | ||
+ | void g() { cout << 2 << ' ';} | ||
+ | void f() { g(); } | ||
+ | }; | ||
+ | |||
+ | class Z: public Y { | ||
+ | public: | ||
+ | void g() { cout << 3 << ' '; } | ||
+ | void f() { g(); } | ||
+ | }; | ||
+ | |||
+ | X x; Y y; Z z; | ||
+ | X *px = &x; Y *py = &y; Z *pz = &z; | ||
+ | |||
+ | void out() { | ||
+ | px->f(); px->g(); py->f(); py-> g(); | ||
+ | cout << '\n'; | ||
+ | } | ||
+ | |||
+ | void F() { | ||
+ | |||
+ | out(); px = py; | ||
+ | out(); py = pz; | ||
+ | out(); | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | 6) В каких из перечисленных ниже языков программирования нет перечислимого типа? | ||
+ | |||
+ | ''Ада'', ''Паскаль'', ''Модула-2'', ''Оберон'', ''Оберон-2'', ''С'', ''С++'', ''C#'' | ||
+ | |||
+ | 7) Написать спецификацию родового (generic) класса в языке ''Java'', который будет | ||
+ | реализовывать очередь. Тела методов писать не нужно. | ||
+ | |||
+ | 8) В каких из перечисленных ниже языков есть возможность раздельной трансляции | ||
+ | вложенных модулей? Привести пример на одном из таких языков. | ||
+ | |||
+ | ''Ада'', ''Паскаль'', ''Модула-2'', ''Оберон'', ''Оберон-2'', ''С'', ''С++'', ''Java'', ''C#'' |
Версия 17:27, 3 февраля 2008
С++
Ада
С# c 2004
Java c 2005
Все современные языки, использующиеся в промышленности, поддерживают идею статической параметризации. Продолжаем рассматривать идею родовых модулей в Аде.
п.1. Родовые модули языки Ада
Ада-83 – принцип РОРИ пронизывает весь этот язык. Получился очень мощный и одновременно эффективный механизм. Порождение нового пакета порождало новый экземпляр данного типа.
package Stack – два параметра – тип элемента размер стека:
generic type T is private; size: integer; package Stack is Push Pop end Stack;
Можно ли так запрограммировать процедуры, чтобы они эффективно работали для любого размера стека? Конечно, да!
Конкретизация:
package IStack is new Stack(integer, 128);
Тривиальная реализация – простая макроподстановка integer и 128. Плюс – крайняя простота. Минус – очень сильное разбухание кода – сколько объявлений, столько и различных процедур. Ада-83 требовала отдельной компиляции спецификации и реализации.
Теперь представим себе другую ситуацию, например пусть у нас есть родовая процедура сортировки:
generic type ElType is private; type Index is range <>; type Arr is array (Index) of ElType; with function <(x, y : T) return Boolean (<>); procedure G_SORT(A: in out Arr; L,R : Index);
Как написать спецификацию той же шаблонной функции на С++, считая, что уже имеется Vector <T>? Вот так:
template <class T, class Index> void Sort(Vector <T>&V, Index L,R);
В процедуре Sort обязательно встретится нечто вроде:
if (v[i]<v[j])
Только во время конкретизации происходит процесс выведения типа и компилятор узнает, есть ли вообще у v операция индексирования или нет. До этого производится только синтаксическая проверка.
В Аде большое разнообразие типов формальных параметров родового модуля. Формальные параметры родовых модулей:
параметр-переменная <=> любое константное выражение
type T is private <=> любой тип с операцией присваивания
type T is range <=> любой дискретный тип с упорядоченностью и функциями «следующий» и «предыдущий»
type T is delta <=> любое плавающее выражение
< > <=> при конкретизации процедуры мы можем не указывать этот вариант (параметр по умолчанию).
Пусть у нас есть:
procedure StrSort is new G_SORT(String, Integer, TARR);
C помощью < > компилятор находит функцию < для строк и подставляет как параметр по умолчанию. Родовые сегменты – абсолютно необходимая в Аде конструкция, потому что там нет передачи процедур и функций по параметру. Как по-другому реализовать функцию, скажем, вычисляющую интеграл? Мы должны передавать вещественный тип-параметр и саму подынтегральную функцию. Допустим, мы вводим новый вид чисел, например комплексный, то есть запись. Мы должны описать тип чисел как приватный и передавать как сравнение, так и всю арифметику. Хотя много всего, но зато будет одна процедура и для вещественных, и для комплексных чисел, и для кватернионов. Компилятор должен видеть не только спецификацию данной абстракции, но и тело. Гибкость повышается, но тело родовой абстракции должно быть доступно в любой момент конкретизации. За в этой жизни надо платить! Либо мы повышаем гибкость, либо производительность. В Аде-83 механизм родовых модулей – единственный, который поддерживал ОО. Как вы думаете, что самое главное, что появилось в Аде-95? Правильно, класс – тегированная запись:
type T is tagged
Какова была основная цель введения дженериков в C# и Java? Для коллекций. Если мы почитаем определение языка C#, то первый шаблон, который мы найдём, это реализация стека:
Elem Stack {}
Раньше же мы видели Object []body; Дальше приведение типа, и если мы пропускаем проверку, получим ошибку времени выполнения. В основном коллекции являются не гетерогенными, и, используя шаблоны, можно сильно увеличить производительность. Самый мощный механизм – шаблоны в С++. Почему же они более мощны и гораздо более сложны, чем в других языках?
Шаблоны в С++:
1) Функции
2) Классы
а) Параметр-класс <=> Любой тип
б) Параметр-переменная <=> Константное выражение
template <class T, int size> class Stack {...}
T – тип данных, size может быть везде, где фигурирует целая константа. Конкретизация происходит явным образом. Stack <int, 256> может появляться везде, где может появляться имя типа. После Stack <int, 256>, увидев Stack <int, 2*128>, компилятор ничего не сгенерирует, т.к. уже один раз сгенерировал. А вот Stack <int, 64> порождает новый класс, хотя в общем-то Push и Pop в этом случае делают одно и то же, что и в случае Stack <int, 256>. Но компилятор мужественно породит новый кусок кода…
Stack <int, 64> S; Вот здесь начинается генерация кода. Но в С++ у нас раздельная трансляция. Шаблоны – очень мощная вещь, но неграмотное их использование неопытным программистом ведет к разбуханию кода… Если одинаковые шаблоны сгенерированы в разных модулях, некоторые компиляторы борются с этим, используя PCH, хотя это делает трансляцию зависимой.
Для функций то же самое, но конкретизация не обязательна. Рассмотрим:
template <class T> void Sort (Vector <T>&V);
Sort порождает бесконечное семейство конкретных реализаций.
Представим, что у нас в программе есть:
Vector <string> X; Sort(X);
В Sort должен явно встречаться код:
if (v[i]<v[j])
Компилятор опять же ничего проверить не может. Эта проверка происходит только в момент конкретизации. В Аде-95 и С++ ситуация одинаковая и не такая, как в Аде-83. В момент конкретизации компилятору надо дать полную информацию обо всём шаблоне, и ни о каком принципе РОРИ речи идти не может. Этим С++ не ограничивается. Пока что было очень похоже на Аду-95. Какой недостаток?
В первых версиях шаблонов в С++ было невозможно:
template <class T> T f() {…}
В новых версиях можно:
Sort <int>(a);
Но это не всё. В С++ есть механизм частичной специализации.
п.2. Шаблоны в С++
Vector <const char *>a; Sort(a);
Будет подставлена функция < для указателей, и ошибки в программе не будет, но работать она будет неправильно.
template <class T> bool less (T& x, T& y) { return x<y; }
Это описание шаблонной функции. Хорошая функция сортировки должна работать через шаблонную функцию сравнения. Но проблема со * остаётся. Что делать? А вот что.
Пишем:
template <> bool less <const char *> (const char *p1, const char *p2) { return strcmp(p1,p2); } template <> class Vector <void *>;
Подставь вместе T void * и сгенерируй код. (?)
Для векторов возможны такие реализации:
1) template <class T> class Vector {…}
2) template <> class Vector <T*>;</br>
Vector <long > V; Vector <int> V1;
Оба по первой реализации и разные.
Vector <const char *> v2; Vector <int *>v3;
Оба по второй и одинаковые.
Будет также эффективно, как и встроенный в язык вектор. Механизм частичной специализации очень эффективен. Физики очень не любили использовать язык С. Компиляторы с ФОРТРАНА были, как ни странно, эффективнее компиляторов с С. С-шные функции требуют слишком много проверок, пример: memcpy (проверяет перекрытие областей памяти и много-много чего прочего). С появлением шаблонов ситуация изменилась. Многие математические библиотеки, такие как Boost, Loki – это именно библиотеки шаблонов – очень производительны. Читай у Александреску про его библиотеку шаблонов Loki. У STL много недостатков, но написана она исключительно производительно в смысле эффективности.
Вспомним:
Find(It first, It last, …)
Для каждого конкретного типа можно написать частичную специализацию Find
- I == X (?)
Можно передавать третьим параметром compare, и вызывать но это неэффективно, так как это срывает работу конвейера. Выход – функтор. Перекрываем операцию ( ):
сlass Finder { public: bool operator ()(T&x, …) { … } }
п.3. C#, Java – обобщенные классы
У них очень простой синтаксис и похожая идея:
class Stack <T> {…} interface IComparable <T> {…}
Параметризовать можно как классы, так и интерфейсы и методы:
public void add <T> (T x) {…}
Слово class просто синтаксически не нужно. Компилятор производит первичный синтаксический анализ и перевод в MIL/Байт-код. Конкретизация же происходит при выполнении кода. Код генерируется оптимальным образом.
В Java в качестве параметризованных типов только классы. Вместо Stack <int> S надо Stack <Integer> S
В C# на такое пойтить не могли. Там же есть структуры! Если аргумент – класс или интерфейс, код разный. Для ссылок одинаковый.
В Java: if (Stack<Integer>.getClass==Stack<String>.getClass()) даст true
В C#:
сlass Dictionary <K, V> { }
K – тип ключа, V – тип значения.
Явно при реализации должно быть нечто вроде:
public void Add( K Key, V Value) { … if (K<K1) … … }
На это будет выдана ошибка. Потому что функции < может не быть.
Надо:
if ((IComparable)K<K1)
Вместо этого можно:
class Dictionary <K, V> where K:Icomparable
Можно теперь ошибки не бояться. Возвращаемся к старой доброй Аде-83.
«Я когда-то за счет изменения нескольких функций выиграл в производительности 30%. Это, извините меня, о-го-го!»
Кстати, по поводу С++. В С++, в отличие от Ады, есть механизм подстановки параметров по умолчанию. new delete
В итоге концепция шаблонов пронизывает все современные языки. Наиболее мощная концепция в С++, но за счёт сложности можно писать очень неэффективные программы. В Java и C# достигнут между производительностью и гибкостью. На этом закончим лекцию и вместе с ней весь курс.
Схема проведения экзамена по Языкам Программирования:
1) Экзамен письменный, длится одну пару
2) Можно пользоваться печатными материалами, если это не типографская продукция
3) Всего восемь задач, максимум за каждую - 6 баллов
4) when баллы>=36 => 5, when 24<=баллы<36 => 4, when 12<=M<25 => 3, default => 2
Задачи досрочного экзамена (19 декабря):
1-ый вариант.
1) В каких из перечисленных ниже языков программирования при обработке исключительных ситуаций используется семантика завершения? Объяснить, что она означает.
Ада, Модула-2, Оберон, Оберон-2, С, С++, Java, C#
2) Объяснить, что означает оператор foreach языка C#. Какие условия накладываются на класс-коллекцию, чтобы он мог использоваться в данном операторе?
3) Объяснить все смыслы ключевого слова final в языке Java.
4) Перечислите способы передачи параметров в языках программирования. Какие из них реализованы в языках Ада-95 и Оберон?
5) Что будет выдано в стандартный канал вывода при вызове функции F()?
class X { public: void g() { cout << 1 << ' '; } virtual void f() { g(); } }; class Y: public X { public: void g() { cout << 2 << ' ';} void f() { g(); } }; class Z: public Y { public: void g() { cout << 3 << ' '; } void f() { g(); } }; X x; Y y; Z z; X *px = &x; Y *py = &y; Z *pz = &z; void out() { px->f(); px->g(); py->f(); py-> g(); cout << '\n'; } void F() { out(); px = py; out(); py = pz; out(); }
6) В каких из перечисленных ниже языков программирования нет перечислимого типа?
Ада, Паскаль, Модула-2, Оберон, Оберон-2, С, С++, C#
7) Написать спецификацию родового (generic) класса в языке Java, который будет реализовывать очередь. Тела методов писать не нужно.
8) В каких из перечисленных ниже языков есть возможность раздельной трансляции вложенных модулей? Привести пример на одном из таких языков.
Ада, Паскаль, Модула-2, Оберон, Оберон-2, С, С++, Java, C#