Связыватели
и адаптеры
* Связывателями
(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) на этапе выполнения. Выбор определяется конкретной
ситуацией — типом объекта, попавшим под родительский перст (указатель) в данный
момент времени.
Forekc.ru
Рефераты, дипломы, курсовые, выпускные и квалификационные работы, диссертации, учебники, учебные пособия, лекции, методические пособия и рекомендации, программы и курсы обучения, публикации из профильных изданий