Абстрактные классы и интерфейсы.




Концептуально понятия класса и интерфейса отличаются в ООП. Интерфейс – это декларация методов (событий) объекта без конкретизации реакций на эти события. С++ не декларирует интерфейс явно, как это делает, например, Java. Но, в качестве альтернативы явному описанию интерфейса, в С++ введены так называемые абстрактные классы.

Абстрактными становятся классы в которых определена одна или более функций-заглушек - виртуальных функций с оператором присвоения значения 0 в их определении, например:

virtual void rotate(int) = 0; // pure virtual method

virtual void rotate(int) = 0 { /* code of pure method */ }; // pure virtual method

Абстрактный класс не может порождать объектов, но может использоваться в качестве базового класса при определении других классов. Поэтому абстрактный класс не должен фигурировать в аргументах функций как тип объектов (но только как тип ссылки или указателя) и в качестве типа возвращаемых функциями значений с теми же оговорками.

Пример абстрактного класса:

class shape { // abstract class

point center;

...

public:

where() { return center; }

move(point p) { center = p; draw(); }

virtual void rotate(int) = 0; // pure virtual function

virtual void draw() = 0; // pure virtual function

virtual void hilite() = 0; // pure virtual function

...

};

 

shape x; // ERROR: attempt to create an object of an abstract class

shape* sptr; // pointer to abstract class is OK

shape f(); // ERROR: abstract class cannot be a return type

int g(shape s); // ERROR: abstract class cannot be a function argument type

shape& h(shape&); // reference to abstract class as return

// value or function argument is OK

Представим, что класс D является непосредственно производным от абстрактного класса B. Тогда каждая виртуальная функция-заглушка класса B, не имеющая переопределения в классе D, становится автоматически функцией заглушкой и для класса D. Из чего следует, что и класс D будет абстрактным.

Пример абстрактного производного класса:

// abstract class circle derived from abstract class shape

class circle: public shape {

int radius; // private

 

public:

void rotate(int) { } // virtual function defined: no action

// to rotate a circle

void draw(); // circle::draw must be defined somewhere

}


 

34.35. Шаблоны функций. Назначение, особенности использования. Примеры. (УМК);

Шаблоны классов. Назначение, особенности использования. Примеры. (УМК);

Шаблоны С++ облегчают генерацию семейств функций или классов, оперирующих со множеством различных типов данных, освобождая Вас от необходимости создавать отдельную функцию или класс для каждого типа. Другими словами, шаблоны предоставляют все удобства для написания общего (родового) определения функции или класса, которое компилятор автоматически транслирует в специфическую версию функции или класса для каждого из типов данных, с которыми программа работает в данный момент.

В определении шаблона может быть задан параметр типа, который обозначает тип переменной или константы, передаваемой через вызов функции. Если программа передает, например, целочисленное значение, то компилятор будет автоматически генерировать версию функции, в которой параметр типа имеет значение int, и это будет вставлено в код, вызывающий данную версию функции.

Шаблоны, в некотором смысле, напоминают макросы, однако они более разнообразны и надежны. Тем не менее, достаточно часто шаблоны реализуются как обычные С-макросы.

Одним из преимуществ применения шаблонов по сравнению с перегружением функций является то, что при использовании шаблона Вам нет необходимости предвидеть, какая версия функции будет запущена при вызове данной программы. Вы просто включите одно определение шаблона, а компилятор будет автоматически генерировать и сохранять в коде лишь те версии функций, которые фактически вызывались.

Нужно отметить, что подавляющее большинство компонентов библиотек С++ базируется на шаблонах.

 

Шаблоны функций.

Определение шаблона функции имеет вид:

template <список_параметров_типов>

тип_возвращаемого_значения имя_функции (список параметров){ /* реализация */ }

Элементы списка_параметров_типов (разделяемые запятыми) определяют типы переменных или констант, передаваемых при вызове функции. Каждый элемент данного списка предваряется ключевым словом class, которое в данном контексте ссылается не на конкретный тип данных class, а на любой тип данных, фактически передаваемый при вызове функции, любой!

Дальнейшее определение шаблона соответствует синтаксису описаний функций, но вместо определенных типов в нем могут быть задействованы элементы списка_параметров_типов.

Пример определения шаблона функции:

template <class T1> T1 add (T1 a,T1 b){

T1 result;

result=a+b;

return result;

}

Инсталляция (инстанцирование) - это создание экземпляров функций по описанному шаблону. Инсталляция может быть явной и неявной. В первом случае в текст программы добавляется прототип функции с аргументами конкретных типов.

Пример явной инсталляции функции:

float add (float a, float b);

Во втором случае инсталляция осуществляется в момент вызова функции с конкретными аргументами. Напрмер:

int a = 10, b = 20, c;

c = add (a, b);

Необходимо только, чтобы для типа-параметра был определен оператор +.

 

Шаблоны классов.

Определение шаблона функции имеет вид:

template <список_параметров_типов> class имя_класса { /* тело класса */ }

В шаблон класса можно включить любое количество параметров типа и параметров констант, помещая их в список_параметров_типов. Внутри определения класса Вы можете использовать параметр типа в любом месте кода, в котором допустимо применение спецификации типа, и параметр константы в любом месте, где допустимо применение константного выражения.

Пример определения шаблона класса:

template <class T> class Vector {

T *data;

int size;

public:

Vector(int);

~Vector() { delete[ ] data; }

T& operator[ ] (int i) { return data[i]; }

};

// Note the syntax for out-of-line definitions.

template <class T> Vector<T>::Vector(int n) {

data = new T[n];

size = n;

};

 

void main() {

Vector<int> x(5); // Generate a vector to store five integers

for (int i = 0; i < 5; ++i)

x[i] = i; // Initialize the vector.

}

В списке_параметров_типов собственно параметры типов указываются с ключевым словом class, а параметры констант - с какими-либо другими ключевыми словами, применяемыми для идентификации типа.

Функции-члены шаблона класса автоматически являются шаблонами функций, т.е. они могут использовать параметр типа шаблона класса, не требуя его явного описания в шаблоне функции. Шаблоны функций, которые являются членами класса, нельзя описывать как virtual. Т.о. реализация функции-члена шаблона класса, которая находится вне определения шаблона класса, должна дополнительно включать следующие два элемента:

Определение должно начинаться с ключевого слова template, за которым следует такой же список_параметров_типов, какой указан в определении шаблона класса.

За именем_класса, предшествующим оператору разрешения видимости, должен следовать список_имен_параметров шаблона.

Пример определение функции-члена шаблона класса:

template <class T, class S> class demo {

T var1;

S var2;

public:

T func(void);

};

template <class T, class S> T demo<T,S>::func(void) {

return var1;

}


36.Обработка исключений. Пример. (УМК);

 

С++ обеспечивает встроенный механизм обработки ошибок, называемый обработкой исключительных ситуаций. Благодаря обработке исключительных ситуаций можно упростить управление и реакцию на ошибки времени исполнения. Обработка исключительных ситуаций в С++ строится с помощью трех ключевых слов: try, catch, throw. Операторы программы, во время выполнения которых вы хотите обеспечить обработку исключительных ситуаций, располагаются в блоке try. Если исключительная ситуация (т.е. ошибка) имеет место внутри блока try, искусственно она генерируется (с помощью throw). Перехватывается и обрабатывается исключительная ситуация с помощью ключевого слова catch. Любой оператор, который генерирует исключительную ситуацию, должен выполняться внутри блока try. (Функции, которые вызываются внутри блока try также могут генерировать исключительную ситуацию). Любая исключительная ситуация должна перехватываться оператором catch, который следует непосредственно за блоком try, генерирующим исключительную ситуацию.

try { // блок try

}

catch (type1 arg1) { // блок catch

}

catch (type2 arg2) { // блок catch

}

...

Блок try должен содержать ту часть программы, в которой вы хотите отслеживать ошибки. Это могут быть несколько операторов внутри одной функции, так и все операторы функции main() (что естественно вызывает отслеживания ошибок во всей программе).

Когда исключительная ситуация возникает, она перехватывается соответствующим ей оператором catсh, который ее обрабатывает. С блоком try может быть связано более одного оператора catch. То, какой конкретно оператор catch используется, зависит от типа исключительной ситуации. Т.е., если тип данных, указанный в операторе catch, соответствует типу исключительной ситуации, то выполняется данный оператор catch. А все другие операторы блока try пропускаются. Если исключительная ситуация перехвачена, то аргумент arg получает ее значение. Можно перехватить любые типы данных, включая и создаваемые вами типы.

Оператор throw должен выполняться либо внутри блока try, либо в любой функции, которую этот блок вызывает. Синтаксически выражение throw появляется в двух формах.

throw; или throw выражение;

Выражение throw устанавливает исключение. Самый внутренний блок try, в котором устанавливается исключение, используется для выбора оператора catch, который обрабатывает исключение. Выражение throw без аргумента повторно устанавливает текущее исключение. Обычно оно используется для дальнейшей обработки исключения вторым обработчиком, вызываемый из первого.

Рассмотрим пример работы исключительной ситуации.

void main(){

try{ throw 10; }

catch(int i) { cout << " error " << i << endl; }

return;

}

На экран выведется сообщение: error 10. Значение целого числа, выданное через throw i, хранится до завершения работы обработчика с целочисленной сигнатурой catch(int). Это значение доступно для использования внутри обработчика в виде аргумента.

Пример переустановки исключения выглядит следующим образом:

catch(int n) {...

throw; // переустановка

}

Поскольку предполагается, что установленное выражение имело целый тип, переустановленное исключение также представляет собой постоянный целый объект, который обрабатывается ближайшим обработчиком, подходящим для этого типа. Концептуально установленное выражение передает информацию в обработчики. Часто обработчики не нуждаются в этой информации. Например, обработчик, который выводит сообщение и аварийно завершает работу, не нуждается ни в какой информации от окружения. Однако, пользователь может захотеть выводить дополнительную информацию или использовать ее для принятия решения относительно действий обработчика. В таком случае допустимо формирование информации в виде объекта.

В С++ блоки try могут быть вложенными. Если в текущем блоке try нет соответствующего обработчика, выбирается обработчик из ближайшего внешнего блока try. Если он не обнаружен и там, тогда используется поведение по умолчанию. catch выглядит подобно объявлению функции с одним параметром без возвращаемого типа:

catch (char *message) { cerr << message << endl; exit(1); }

catch (...) { // действие, которое нужно приянть по умолчанию.

cerr << “ That’s all folks” << andl; abort();

}

В списке аргументов допускается сигнатура, которая соответствует любому параметру. Кроме того, формальный параметр может быть абстрактным объявлением. Это значит, что он может иметь информацию о типе без имени переменной. Обработчик вызывается соответствующим выражением throw. При этом, фактически, происходит выход из блока try. Система вызывает функции освобождения, которые включают деструкторы для любых объектов, локальных для блока try.

Синтаксически Спецификация исключения представляет собой части объявления функции и имеет форму:

заголовок_функции throw (список типов)

Список типов - это список типов, которые может иметь выражение throw внутри функции. Если этот список пуст, компилятор может предположить, что throw не будет выполняться функцией.

void foo() throw(int,over_flow);

void noex(int i) throw();

Если спецификация исключения оставлена, тогда возникает допущение, что такой функцией может быть установлено произвольное исключение. Хорошей практикой программирования будет показать с помощью спецификации, какие ожидаются исключения.

Когда существует много определенных ошибочных ситуаций в программе, может быть использована иерархия классов исключений. Эти иерархии позволяют соответственно упорядоченному множеству catch обрабатывать исключения в логической последовательности. Тип базового класса в списке catch должен следовать после типа порожденного.

Восстановление при возникновении ошибок - в основном имеет отношение к правильности написания программы. Восстановление при возникновении ошибок основывается на передачи управления. Недисциплинированная передача управления ведет к хаосу. При восстановлении отказов предполагается, что исключение нарушило вычисления. Продолжать вычисления становится опасно. Обработка исключений влечет за собой упорядоченное восстановление при появлении отказа. В большинстве случаев программирование, которое вызывает исключения, должно выводить диагностическое сообщение и элегантно завершать работу. При специальных формах обработки, типа работы в режиме реального времени и при отказоустойчивом вычислении существует необходимость в том, чтобы система не приостанавливалась. В таких случаях героические попытки восстановления узаконены. С++ использует модель завершения, которая вынуждает завершать текущий блок try. При этом режиме пользователь или повторяет код, игнорируя исключение, или подставляет результат по умолчанию и продолжает. При повторении кода более вероятно получить правильный результат. Опыт показывает, что обычно код слабо комментируется. Хорошо спланированный набор ошибочных условий, обнаруживаемых пользователями абстрактных типов данных – важная часть проекта. Если при нормальном программировании слишком часто обнаруживаются ошибки и происходит прерывание - это признак того, что программа плохо обдумана.


 

39.40.41 Стандартная библиотека шаблонов для C++ STL, назначение и состав STL. (УМК);

 

Библиотека STL содержит пять основных видов компонентов:

- алгоритм (algorithm): определяет вычислительную процедуру.

- контейнер (container): управляет набором объектов в памяти.

- итератор (iterator): обеспечивает для алгоритма средство доступа к содержимому контейнера.

- функциональный объект (function object): инкапсулирует функцию в объекте для использования другими компонентами.

- адаптер (adaptor): адаптирует компонент для обеспечения различного интерфейса.

Такое разделение позволяет нам уменьшить количество компонентов. Например, вместо написания функции поиска элемента для каждого вида контейнера мы обеспечиваем единственную версию, которая работает с каждым из них, пока удовлетворяется основной набор требований.

В STL определены два типа контейнеров: последовательности и ассоциативные. Ключевая идея для стандартных контейнеров заключается в том, что когда это представляется разумным, они должны быть логически взаимозаменяемыми. Пользователь может выбирать между ними, основываясь на соображениях эффективности и потребности в специализированных операциях. Например, если часто требуется поиск по ключу, можно воспользоваться map (ассоциативным массивом). С другой стороны, если преобладают операции, характерные для списков, можно воспользоваться контейнером list. Если добавление и удаление элементов часто производится в концы контейнера, следует подумать об использовании очереди queue, очереди с двумя концами deque, стека stack. По умолчанию пользователь должен использовать vector; он реализован, чтобы хорошо работать для самого широкого диапазона задач.

В STL определены следующие классы-контейнеры (в угловых скобках указаны заголовочные файлы, где определены эти классы):

bitset множество битов <bitset.h>
vector динамический массив <vector.h>
list линейный список <list.h>
deque двусторонняя очередь <deque.h>
stack стек <stack.h>
queue очередь <queue.h>
priority_queue очередь с приоритетом <queue.h>
map ассоциативный список для хранения пар ключ/значение <map.h>
multimap с каждым ключом связано два или более значений <map.h>
set множество <set.h>
multiset множество, в котором элемент не обязательно уникален <set.h>

 

Контейнер vector. Вектор vector в STL определен как динамический массив с доступом к его элементам по индексу.

vector<int> a; vector<double> x(5); vector<char> c(5,’*’); vector<int> b(a);

Для любого объекта, который будет храниться в векторе, должен быть определен конструктор по умолчанию. Кроме того, для объекта должны быть определены операторы < и ==.

Для класса вектор определены следующие операторы сравнения: ==, <, <=,!=, >, >=. Кроме этого, для класса vector определяется оператор индекса []. Новые элементы могут включаться с помощью функций insert(), push_back(), resize(), assign(). Существующие элементы могут удаляться с помощью функций erase(), pop_back(), resize(), clear(). Доступ к элементам осуществляется с помощью итераторов begin(), end(), rbegin(), rend(). Манипулирование контейнером, сортировка, поиск в нем и тому подобное возможно с помощью глобальных функций <algorithm.h>. Пример:

#include<iostream.h>

#include<vector.h>

using namespace std;

void main(){

vector<int> v; int i;

for(i=0;i<10;i++) v.push_back(i);

for(i=0;i<10;i++)v[i]=v[i]+v[i];

for(i=0;i<v.size();i++) cout<<v[i]<<“ ”; cout<<endl; }

 

Пример – Доступ к вектору через итератор:

#include<iostream.h>

#include<vector.h>

using namespace std;

void main(){

vector<int> v; int i;

for(i=0;i<10;i++)v.push_back(i); cout<<“size=”<<v.size()<<“\n”;

vector<int>::iterator p=v.begin();

while(p!=v.end()) {cout<<*p<<” “;p++;}

}

 

Пример – Вектор содержит объекты пользовательского класса:

#include<iostream.h>

#include<vector.h>

#include”student.h”

using namespace std;

void main() {

vector<STUDENT> v(3); int i;

v[0]=STUDENT(“Иванов”,45.9);

v[1]=STUDENT(“Петров”,30.4);

v[0]=STUDENT(“Сидоров”,55.6);

for(i=0;i<3;i++) cout<<v[i]<<“ ”; cout<<endl; //вывод

}

 

Ассоциативные контейнеры (массивы). Ассоциативный массив содержит пары значений. Зная одно значение, называемое ключом (key), мы можем получить доступ к другому, называемому отображенным значением (mapped value). Ассоциативный массив можно представить как массив, для которого индекс не обязательно должен иметь целочисленный тип: V& operator[](const K&) возвращает ссылку на V, соответствующий K.

Ассоциативные контейнеры – это обобщение понятия ассоциативного массива. Ассоциативный контейнер map - это последовательность пар (ключ, значение), которая обеспечивает быстрое получение значения по ключу. Типичная операция для ассоциативного контейнера – это ассоциативный поиск при помощи операции индексации ([]).

Множества set можно рассматривать как ассоциативные массивы, в которых значения не играют роли, так что мы отслеживаем только ключи.

Стандартная библиотека шаблонов для С++ STL. Использование алгоритмов. Привести примеры кода.

Библиотека STL содержит пять основных видов компонентов:

- алгоритм (algorithm): определяет вычислительную процедуру.

- контейнер (container): управляет набором объектов в памяти.

- итератор (iterator): обеспечивает для алгоритма средство доступа к содержимому контейнера.

- функциональный объект (function object): инкапсулирует функцию в объекте для использования другими компонентами.

- адаптер (adaptor): адаптирует компонент для обеспечения различного интерфейса.

Такое разделение позволяет нам уменьшить количество компонентов. Например, вместо написания функции поиска элемента для каждого вида контейнера мы обеспечиваем единственную версию, которая работает с каждым из них, пока удовлетворяется основной набор требований.

Каждый алгоритм выражается шаблоном функции или набором шаблонов функций. Таким образом, алгоритм может работать с очень разными контейнерами, содержащими значения разнообразных типов. Алгоритмы, которые возвращают итератор, как правило, для сообщения о неудаче используют конец входной последовательности. Алгоритмы не выполняют проверки диапазона на их входе и выходе. Когда алгоритм возвращает итератор, это будет итератор того же типа, что и был на входе. Алгоритмы в STL реализуют большинство распространенных универсальных операций с контейнерами, такие как просмотр, сортировка, поиск, вставка и удаление элементов.

Алгоритмы определены в <algorithm.h>. Ниже приведены имена некоторых наиболее часто используемых функций-алгоритмов STL.



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-08-20 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: