Бьерн Страуструп. Язык программирования С++


Передача операций как параметров функций



бет69/124
Дата16.07.2016
өлшемі3.27 Mb.
#204081
түріКнига
1   ...   65   66   67   68   69   70   71   72   ...   124

8.4.3 Передача операций как параметров функций


Можно не задавать функцию сравнения как часть типа

Vector, а передавать ее как второй параметр функции sort().

Этот параметр является объектом класса, в котором определена

реализация операции сравнения:
template void sort(Vector& v, Comparator& cmp)

{

unsigned n = v.size();


for (int i = 0; i

for ( int j = n-1; i

if (cmp.lessthan(v[j],v[j-1])) {

// меняем местами v[j] и v[j-1]

T temp = v[j];

v[j] = v[j-1];

v[j-1] = temp;

}

}


Этот вариант можно рассматривать как обобщение традиционного приема,

когда операция сравнения передается как указатель на функцию.

Воспользоваться этим можно так:
void f(Vector& vi,

Vector& vc,

Vector& vi2,

Vector& vs)

{

Comparator ci;



Comparator cs;

Comparator cc;


sort(vi,ci); // sort(Vector&);

sort(vc,cc); // sort(Vector&);

sort(vi2,ci); // sort(Vector&);

sort(vs,cs); // sort(Vector&);

}
Отметим, что включение в шаблон класса Comparator как параметра

гарантирует, что функция lessthan будет реализовываться подстановкой.

В частности, это полезно, если в шаблонной функции используется

несколько функций, а не одна операция сравнения, и особенно это

полезно, когда эти функции зависят от хранящихся в том же объекте

данных.


8.4.4 Неявная передача операций


В примере из предыдущего раздела объекты Comparator на самом деле

никак не использовались в вычислениях. Это просто "искусственные"

параметры, нужные для правильного контроля типов. Введение таких

параметров достаточно общий и полезный прием, хотя и не слишком

красивый. Однако, если объект используется только для передачи

операции (как и было в нашем случае), т.е. в вызываемой функции

не используется ни значение, ни адрес объекта, то можно вместо этого

передавать операцию неявно:
template void sort(Vector& v)

{

unsigned n = v.size();


for (int i=0; i

for (int j=n-1; i

if (Comparator::lessthan(v[j],v[j-1])) {

// меняем местами v[j] и v[j-1]

T temp = v[j];

v[j] = v[j-1];

v[j-1] = temp;

}

}


В результате мы приходим к первоначальному варианту использования

sort():
void f(Vector& vi,

Vector& vc,

Vector& vi2,

Vector& vs)

{
sort(vi); // sort(Vector&);

sort(vc); // sort(Vector&);

sort(vi2); // sort(Vector&);

sort(vs); // sort(Vector&);

}
Основное преимущество этого варианта, как и двух предыдущих, по

сравнению с исходным вариантом в том, что часть программы, занятая

собственно сортировкой, отделена от частей, в которых находятся

такие операции, работающие с элементами, как, например lessthan.

Необходимость подобного разделения растет с ростом программы, и

особенный интерес это разделение представляет при проектировании

библиотек. Здесь создатель библиотеки не может знать типы параметров

шаблона, а пользователи не знают (или не хотят знать) специфику

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

sort() использовался более сложный, оптимизированный и рассчитанный

на коммерческое применение алгоритм, пользователь не очень бы

стремился написать свою особую версию для типа char*, как это было

сделано в $$8.4.1. Хотя реализация класса Comparator для специального

случая char* тривиальна и может использоваться и в других ситуациях.

8.4.5 Введение операций с помощью параметров шаблонного класса


Возможны ситуации, когда неявность связи между шаблонной функцией

sort() и шаблонным классом Comparator создает трудности. Неявную

связь легко упустить из виду и в то же время разобраться в ней

может быть непросто. Кроме того, поскольку эта связь "встроена"

в функцию sort(), невозможно использовать эту функцию для

сортировки векторов одного типа, если операция сравнения рассчитана

на другой тип (см. упражнение 3 в $$8.9). Поместив функцию sort()

в класс, мы можем явно задавать связь с классом Comparator:
template class Sort {

public:


static void sort(Vector&);

};
Не хочется повторять тип элемента, и это можно не делать, если

использовать typedef в шаблоне Comparator:
template class Comparator {

public:


typedef T T; // определение Comparator::T

static int lessthan(T& a, T& b) {

return a < b;

}

// ...



};
В специальном варианте для указателей на строки это определение

выглядит так:


class Comparator {

public:


typedef char* T;

static int lessthan(T a, T b) {

return strcmp(a,b) < 0;

}

// ...



};
После этих изменений можно убрать параметр, задающий тип элемента,

из класса Sort:


template class Sort {

public:


static void sort(Vector&);

};
Теперь можно использовать сортировку так:


void f(Vector& vi,

Vector& vc,

Vector& vi2,

Vector& vs)

{

Sort< int,Comparator >::sort(vi);



Sort< String,Comparator >:sort(vc);

Sort< int,Comparator >::sort(vi2);

Sort< char*,Comparator >::sort(vs);

}
и определить функцию sort() следующим образом:


template

void Sort::sort(Vector& v)

{

for (int i=0; i

for (int j=n-1; i

if (Comp::lessthan(v[j],v[j-1])) {

T temp = v[j];

v[j] = v[j-1];

v[j-1] = temp;

}

}


Последний вариант ярко демонстрирует как можно соединять в одну

программу отдельные ее части. Этот пример можно еще больше

упростить, если использовать класс сравнителя (Comp) в качестве

единственного параметра шаблона. В этом случае в определениях класса

Sort и функции Sort::sort() тип элемента будет обозначаться как Comp::T.



Достарыңызбен бөлісу:
1   ...   65   66   67   68   69   70   71   72   ...   124




©dereksiz.org 2024
әкімшілігінің қараңыз

    Басты бет