Вернемся к примеру из предыдущего параграфа. Представим, что мы поразмыслили и решили, что в будущем для нас может стать актуальным быстродействие и необходимость реализации C++ API. Сводим эти критерии в таблицу с инверсной шкалой, считаем, что важность этих критериев одинакова. Подсчитываем сумму (Табл. 11).
Табл. 11. Интегральные оценки с инверсной шкалой
Получившиеся результаты суммируем с результатами, полученными с использованием обычной шкалы (Табл. 12).
Табл. 12. Поправки с учетом инверсной шкалы
Итак, после внесенных поправок для прогнозных показателей максимальное количество балов набирает указатель на функцию, который рекомендуется к применению.
Может оказаться, что даже после учета прогнозных показателей остаются реализации с одинаковым количеством баллов. В этом случае выбор остается на усмотрение разработчика. Он может, к примеру, взять критерий, который лично для него является более предпочтительным (например, простота), и выбрать реализацию по этому критерию. Или просто выбрать, что называется, первую попавшуюся.
3.4. Итоги
Сравнительный анализ реализаций обратных вызовов необходим для выбора наилучшей в конкретной ситуации. Методика анализа включает в себя выбор объектов, определение критериев сравнения, построение матрицы соответствия, выбор оптимального решения.
Качественный анализ используется, если необходимо выбрать реализацию, оптимальную по какому-нибудь единственному критерию. Если у нас имеется несколько критериев, то необходим количественный анализ, в качестве которого применяется метод интегральных оценок.
Рассмотренные методики подходят не только для исследования обратных вызовов, их можно применять в любых других случаях, когда необходим выбор оптимального архитектурного решения из множества возможных.
4. Обратные вызовы и шаблоны
4.1. Общие понятия шаблонов
Шаблоны в C++ являются инструментом, реализующим параметрический полиморфизм, что означает возможность построения единого (обобщенного) кода для различных типов данных16. В таком коде не задаются конкретные типы, а вводятся параметры, в которые затем подставляется нужный тип данных. Чтобы код работал корректно, типы должны удовлетворять некоторым соглашениям, или, другими словами, поддерживать определенный интерфейс.
Обобщенный код – это код, реализующий заданную функциональность без привязки к типам данных.
Шаблоны объявляются ключевым словом template, после которого в угловых скобках перечисляются параметры. Параметрами шаблона могут быть как типы данных, так и значения.
Пример объявления шаблона:
template SomeTemplate<typename type, int value>
Здесь объявлен шаблон с одним параметром-типом type и параметром-значением value.
Параметрам шаблона, как типам, так и значениям, могут быть назначены значения по умолчанию:
template SomeTemplate<typename type = SomeStruct(), int value = 0>
После объявления шаблона следует код шаблона, в качестве которого выступает функция либо класс. В этом коде вместо имен типов и числовых значений можно подставлять имена параметров. Конкретные типы и значения, подставляемые в эти параметры, станут известны после инстанциирования шаблона, под которым понимается объявление экземпляра шаблона с заданными типами.
Инстанциирование шаблона – это объявление экземпляра шаблона с заданными типами.
Инстанциирование шаблона может быть явным и неявным. При явном инстанциировании типы параметров шаблона объявляются, а при неявном – выводятся, исходя из типов входных аргументов. Пример объявления шаблонов и их инстанциирование представлены в Листинг 23.
template<typename type, int size = 1> // (1)
class StaticArray
{
public:
type array[size];
};
template <typename TYPE> // (2)
TYPE Sum(TYPE s2, TYPE s3)
{
return s2 + s3;
}
int main()
{
StaticArray<int, 1> someArray; // (3)
int a = 0; double x = 8;
Sum(a, a); // (4)
Sum<double> (a, x); // (5)
}
16
В противоположность полиморфизму подтипов, который подразумевает исполнение потенциально разного кода для каждого типа или подтипа. В C++ полиморфизм подтипов реализуется с помощью наследования и виртуальных функций.
Термины «параметрический полиморфизм» и «полиморфизм подтипов» больше характерны для академической литературы, в C++ обычно используются их эквиваленты «статический полиморфизм» и «динамический полиморфизм». С точки зрения теории, такая терминология не совсем корректна, потому что она скорее отражает не сущность полиморфизма, а способ его реализации в конкретном языке программирования. Тем не менее, в C++ эти термины прижились.