Лекция 6. Шаблоны. Обработка исключительных ситуаций.


1. Обобщенные (шаблонные) функции.

Рассмотрим задачу: обмен значениями между двумя переменными.


Пример 1. Функция, обменивающие значения двух целых переменных.


void swapint(int &a, int &b) {

int temp;

temp = a;

a = b;

b = temp;

}


Пример 2. Функция, обменивающие значения двух вещественных переменных.


void swapвdouble(double &a, double &b) {

double temp;

temp = a;

a = b;

b = temp;

}


В языке C++ есть возможность не реализовывать все функции вручную, а поручить это компилятору:


Пример 3. Шаблон функции, меняющей местами значения двух переменных любых типов


#include <iostream>

using namespace std;


template <class X> void swapargs(X &a, X &b) {

X temp;

temp = a;

a = b;

b = temp;

}

int main() {

int i = 10, j = 20;

double x = 10.1, y = 23.3;

char a = 'x', b = 'z';


swapargs(i, j);

swapargs(x, y);

swapargs(a, b);


cout << “i: “ << i << “, j: “ << j << “\n”;

cout << “x: “ << x << “, y: “ << y << “\n”;

cout << “a: “ << a << “, b: “ << b << “\n”;


return 0;

}


Результатом работы данной программы будет следующий текст:


i: 20, j: 10

x: 23.3, y: 10.1

a: z, b: x


Реализация трех функций swapargs() (с целыми, вещественными и символьными аргументами) ложится на плечи компилятора. Компилятор сгенерирует код тех функций, которые вызваны, прямо во время компиляции.


Шаблон обобщенной функции может иметь столько параметров, сколько потребуется.


Пример 4. Функция с двумя обобщенными типами


#include <iostream>

using namespace std;


template <class type1, class type2>

void myfunc(type1 x, type2 y) {

cout << x << “ “ << y << “\n”;

}


int main() {

myfunc(10, “I love C++”);

myfunc(98.6, 19L);

return 0;

}


При генерации конкретных экземпляров функции myfunc() компилятор заменяет шаблонные типы type1 и type2 типами int и char*, а также double и long соответственно.


Обобщенные классы

Пример 5. Обобщенный класс stack


#include <iostream>

using namespace std;

const int SIZE = 10;

template <class StackType> class stack {

{

StackType stck[SIZE]; // Содержит элементы стека

int tos; // Индекс вершины стека


stack() { tos = 0; }

void push(StackType ob) {

if (tos >= SIZE) {

cout << “Стек полон\n”;

return;

}

stck[tos++] = ob;

}

StackType pop() {

if (tos <= 0) {

cout << “Стек пуст\n”;

return 0;

}

return stck[--tos];

}

};


int main() {

stack<char> s1, s2;

int i;

s1.push('a');

s2.push('x');

s1.push('b');

s2.push('y');

s1.push('c');

s2.push('z');

for (i = 0; i < 3; i++) cout << “Pop from s1: “ << s1.pop() << “\n”;

for (i = 0; i < 3; i++) cout << “Pop from s2: “ << s2.pop() << “\n”;


stack<double> d1, d2;

d1.push(1.1);

d2.push(2.2);

d1.push(3.3);

d2.push(4.4);

d1.push(5.5);

d2.push(6.6);

for (i = 0; i < 3; i++) cout << “Pop from d1: “ << d1.pop() << “\n”;

for (i = 0; i < 3; i++) cout << “Pop from d2: “ << d2.pop() << “\n”;


return 0;

}


Шаблонный класс может иметь несколько обобщенных типов. Для этого их надо перечислить в списке шаблоннных параметров через запятую.


Пример 6. Шаблонный класс с двумя параметрами.


template <class Type1, class Type2> class myclass {

Type1 i;

Type2 j;

public:

myclass(Type1 a, Type2 b) {i = a; j = b; }

void show() { cout << i << “ “ << j << “\n”; }

};

int main() {

myclass<int, double> ob1(10, 0.23);

myclass<char, char*> ob2('X', “I love templates!”);

ob1.show();

ob2.show();

return 0;

}


Эта программы выводит следующий текст:


10 0.23

X I love templates


В качестве параметров шаблона можно использовать стандартные типы:


Пример 7. Реализация безопасных обобщенных массивов.


#include <iostream>

#include <cstdlib>

using namespace std;


template <class Atype, int size> class atype {

Atype a[size];

public:

atype() {

register int i;

for (i = 0; i < size; i++) a[i] = i;

}

Atype &operator[](int i) {

if (i < 0 || i > size – 1) {

cout << “\nInvalid index value: “ << i << “\n”;

exit(1);

}

return a[i];

}

};

int main() {

atype<int, 10> intob;

atype<double, 15> doubleob;

int i;

cout << “Integer array: “

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

for (i = 0; i < 10; i++) cout << intob[i] << “ “;

cout << “\n”;


cout << “Double array: “

for (i = 0; i < 15; i++) doubleob[i] = I;

for (i = 0; i < 15; i++) cout << doubleob[i] << “ “;

cout << “\n”;


intob[12] = 100; // Prints error message

return 0;

}


2. Исключения.


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

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


Общий вид конструкции механизма исключительных ситуаций:


try {

}

catch (тип1 аргумента) {

}

catch (тип2 аргумента) {

}

catch (типN аргумента) {

}

catch(…) { // все оставшиеся типы

...

}


Пример 1.


#include <iostream>

using namespace std;

int main() {

cout << “Начало\n”;

try {

cout << “Внутри блока try\n”;

throw 100; // Генерируем ошибку

cout << “Этот оператор не выполняется\n”;

} catch(int I) {

cout << «Исключительная ситуация, i “ << i << “\n”;

}

cout << «Конец\n”;

return 0;

}


Эта программа печатает на экран следующий текст:


Начало

Внутри блока try

Исключительная ситуация, i: 100

Конец


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

#include <iostream>

using namespace std;

void divide(double a, double b);

int main() {

{

double i, j;

do {

cout << “Введите числитель (0 означает выход): «;

cin >> i;

cout << “Введите знаменатель: ”;

cin >> j;

divide(i, j);

} while (i != 0);

return 0;

}

void divide(double a, double b) {

try {

if (!b) throw b; // Проверка деления на нуль

cout << “Результат: ” << a / b << “\n”;

} catch (double b) {

cout << “Делить на нуль нельзя\n”

}

}


Приведенный код на введенные числа, например, 5 и 0, выведет сообщение Делить на нуль нельзя».