Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Итог_Пособие C++.doc
Скачиваний:
7
Добавлен:
01.07.2025
Размер:
2 Мб
Скачать

5.5 Разработка шаблонных классов с настраиваемой функциональностью

Рассмотрим следующую задачу. В пунктах 5.2-5.3 у нас приводился пример шаблонного класса PasArray. Пусть нужно добавить в этот класс проверку выхода индекса за границы массива. При этом есть два дополнительных требования:

1). Одновременно в программе могут быть объекты, где такая проверка выполняется, и объекты, где она не выполняется (например, для повышения производительности). Отсюда следует, что с помощью директив условной компиляции данная задача по-простому не решается.

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

С помощью сочетания обобщённого и объектно-ориентированного программирования можно написать следующее решение:

// Пример 5.10 - использование шаблонных классов-стратегий

class CheckBounds { //стратегия "проверять выход за границы"

protected:

static void check(int index, int left, int size) {

if (index < left || index > left + size - 1)

throw std::out_of_range("index is out of bounds");

}

};

class NoCheckBounds { //стратегия "не проверять выход за границы"

protected:

static void check(int, int, int) {} // ничего не делаем

};

template<class T, class CheckingPolicy = NoCheckBounds>

class PasArray : public CheckingPolicy {

int left;

std::vector<T> data;

public:

PasArray(int left, int right) :

left(left),

data (right - left + 1) {}

typename std::vector<T>::reference operator [] (int index) {

this->check(index, left, data.size());

return data[index - left];

}

};

int main() {

PasArray<int, CheckBounds> a(-5, 5);

try {

a[-6] = 8; // будет возбуждено исключение

} catch (std::out_of_range) {

std::cout << "Выход за границы\n";

}

// по умолчанию используется NoCheckBounds

PasArray<int> b(-5, 5);

b[-6] = 8; // неопределённое поведение программы

}

Объявление PasArray<int, CheckBounds> говорит о том, что при таком инстанцировании класс PasArray будет наследоваться от класса CheckBounds, в котором имеется проверка выхода за границу. Объявление PasArray<int, NoCheckBounds> или просто PasArray<int> говорит, что при таком инстанцировании PasArray будет наследоваться от класса NoCheckBounds, где такой проверки нет.

Более детальные сведения о подобном использовании шаблонов можно найти, например, в [8].

Примечание. По сути, мы привели пример реализации паттерна проектирования "Стратегия". Подробнее о том, что такое паттерны проектирования, и какие ещё паттерны бывают, смотрите в следующей главе.

5.6 Использование шаблонов для вычислений на этапе компиляции

Как уже говорилось в пункте 5.1, при объявлении шаблонов можно использовать не только фиктивные типы, но и параметры обычных типов. Это позволяет получить интересные результаты – например, реализовать выполнение достаточно сложных вычислений не на этапе выполнения программы, а на стадии её компиляции. В качестве примера приведём код для вычисления факториала целого числа (пример из статьи https://habrahabr.ru/post/118429).

// Пример 5.11 - вычисление факториала на стадии компиляции

#include <iostream>

template<int n>

struct Factorial {

static const int f = Factorial<n - 1>::f * n;

};

template<>

struct Factorial<0> {

static const int f = 1;

};

int main() {

std::cout << Factorial<5>::f << std::endl; // будет выведено 120

}

Здесь интересно то, что факториал вычисляется не во время выполнения программы, а на стадии её компиляции. Для генерации шаблона Factorial<5> должен сгенерироваться шаблон Factorial<4>, и т.д. Такая рекурсивная генерация шаблонов заканчивается на Factorial<0>, для которого в поле f записывается единица.

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