Рассмотрим задачу: обмен значениями между двумя переменными.
Пример 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;
}
Механизм исключительных ситуаций позволяет удобно реализовать обработку ошибок, возникающие в ходе выполнения программы. Он основан на ключевых словах 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, выведет сообщение “Делить на нуль нельзя».