* Связывателями (binders) называются
вспомогательные шаблоны функций, которые создают
некий объект (adaptor) , подстраивающий или
преобразующий бинарный функциональный объект в
унарный путем привязывания недостающего
аргумента. Звучит запутанно, но суть достаточно
проста. Представьте, что надо найти в нашей
последовательности людей первого человека,
который моложе, чем
Man win("Winton
Kelly", 50);
Для объектов класса Man уже определена бинарная
операция operator< (), которой пользуется
предикат less<Man> (), и мы показали
использование этого предиката в алгоритме
сортировки по возрасту. В том примере функция
sort сама подставляла оба параметра в бинарную
функцию operator< (), сравнивая объекты для нужд
сортировки. Теперь мы используем связыватель
bind2nd, для того чтобы зафиксировать
(привязать) второй параметр этой функции и
сделать его равным объекту win. Первый параметр
при этом остается свободным, он будет пробегать
по всему контейнеру. Таким образом, мы сможем
сравнить все объекты последовательности с одним
фиксированным объектом win.
В этом же фрагменте мы покажем, как использовать
другой адаптер mem_f un_ref, который тоже
является вспомогательным шаблоном функции для
вызова какой-либо функции, являющейся членом
класса, в нашем случае Man. Вызов осуществляется
для всех объектов класса в процессе прохода по
контейнеру. Введите в состав класса Man две
public-функции, выделяющие имя и фамилию
человека. В коде этих функций попутно
демонстрируются методы класса string, которые
позволяют осуществлять поиск и выделение
подстроки:
//======== Выделяем имя
Man FirstName()
{
//======== Ищем первое вхождение пробела
int
pos = m_Name.find_first_of(string(" "),0);
string name = m_Name.substr(0, pos);
cout
« '\n' « name;
return *this;
}
//======== Выделяем фамилию
Man SurName()
{
//======== Ищем последнее вхождение пробела
int
pos = m_Name.find_last_of(" "), num =
m_Name.length () - pos;
string name = m_Name.substr(pos + 1, num);
cout
« '\n' « name;
return *this;
}
Вектор заполняется элементами, взятыми из
массива а г, и при этом используется метод
assign, который стирает весь массив и вновь
заполняет его элементами, копируя их из
диапазона памяти, задаваемого параметрами. Далее
мы показываем, как используется связыватель
bind2nd и адаптер члена-функции mem_f un_ref:
void
main ()
{
Man ar[] =
{
joy, joe, zoran,
тагу,
simon, liza, Man("Lina Groves", 19)
};
uint size =
sizeof(ar)/sizeof(Man);
vector<Man> men;
men.assign(ar,
ar+size);
pr(men,"Man Vector");
//======= Привязка второго аргумента
vector<Man>::iterator p = find_if(men.begin(),
men.end(),
bind2nd(less<Man>(), win));
if
(p != men.end())
cout
« "\nFound a man less than " « win « "\n\t" «
*p;
//======= Использование метода класса (mem_fun_ref)
cout
« "\n\nMen Names:\n";
for_each (men.begin(),
men.end(), mem_fun_ref(&Man::SurName));
cout
« "\n\nMen First Names:\n";
for_each (men.begin
(), men.end(), mem_fun_ref(&Man::FirstName));
cout
« "\n\n";
}
Напомним, что для успешной работы вы должны
вставить в функцию main тот набор объектов
класса Man, который был приведен ранее.
Примечание
При анализе этого кода бросается в глаза
неестественность прототипов функций SurName и
FirstName. Логика использования этих функций
совсем не требует возвращать какое-либо
значение, будь то Man, или переменная любого
другого типа. Естественным выбором будет
прототип void SurNameQ;. Но, к сожалению, этот
выбор не проходит по неизвестным мне причинам ни
в Visual Studio б, ни в Studio.Net 7.O. Я
достаточно много времени потратил на бесполезные
поиски ответа на этот вопрос и пришел к выводу,
что это ошибка разработчиков. В подтверждение
такого вывода приведу следующие аргументы.
Во-первых, измените тип возвращаемого значения
на любой другой, но не void, и программа будет
работать. Например, возьмите прототип string
SurName(); и возвращайте return "MicrosoftisOK";
(или другую пару: int и-127). Во-вторых, все
примеры на (mem_fun_ref) в документации MSDN
возвращают загадочный bool. В-третьих, в
документации SGI (Silicon Graphics) приведены
аналогичные примеры с функциями, возвращающими
void. Там, как вы знаете, используется другая
платформа (IRIS). В-четвертых, наш пример (без
void) проходит в Visual Studio б и не работает в
бета-версии Studio.Net. Будем надеяться, что
ситуация со временем выправится.
Адаптер mem_fun в отличие от mem_fun__ref
используется с контейнерами, хранящими указатели
на объекты, а не сами объекты. Хорошим примером
использования mem_f un, в котором иллюстрируется
полиморфизм позднего связывания (late binding
polymorphism), является следующий:
//========
Базовый класс. К
сожалению, абстрактным
//=======
его не позволит
сделать контейнер
struct
Stud
virtual bool
print()
{
cout
« "\nl'm a Stud";
return true;
}
};
//=====
Производный класс
struct GoodStud : public Stud
{
bool print ()
{
cout
« "\nl'm a Good Stud";
return
true;
}
};
//=======
Еще один
производный класс
struct
BadStud : public Stud
{
bool
print ()
{
cout
« "XnI'm a Bad Stud";
return true;
}
};
//=======
Иллюстрируем полиморфизм в действии
void
main () {
//====== Вектор указателей типа Stud*
vector<Stud*> v;
//======
Они могут указывать и на детей
v.push_back (new
StudO);
v.push_back (new
GoodStudO);
v.push_back(new
BadStud(J);
//======
Выбор тела метода
происходит поздно
//======
на этапе выполнения
for_each(v.begin(),
v.end(), mem_fun(&Stud:: print));
cout
<<"\n\n";
}
Конечно же, эта программа выведет:
I'm a Stud
I'm a Good Stud
I'm a Bad Stud
так как mem_fun будет вызвана с помощью
указателя типа stud* (на базовый класс) —
непременное условие проявления полиморфизма, то
есть выбора конкретной версии виртуальной
функции (адреса функции из vtable) на этапе
выполнения. Выбор определяется конкретной
ситуацией — типом объекта, попавшим под
родительский перст (указатель) в данный момент
времени. |