|
Программирование >> Включение нужных заголовков
Пара примеров: tempiate<typename Т> class MeetsThreshold: public std::unary function<Widget,bool>{ private; const T threshold; public: Meets Threshold(const T& threshold); bool operatorO (const WidgetS) const: struct WidgetNameCompare: std::bi nary functi on<Widget,Widget,bool>{ bool operatorO(const WidgetS Ihs,const WidgetS rhs) const; В обоих слзаях типы, передаваемые unary f uncti on или binary f uncti on, совпадают с типами, ползаемыми и возвращаемыми функцией operatorO класса функтора, хотя на первый взгляд несколько странно, что тип возвращаемого значения operatorO передается в последнем аргументе unary f uncti on или binaryf uncti on. Возможно, вы заметили, что MeetsThreshol d является классом, а Wi dgetNameCompare является структурой. MeetsThreshol d обладает внутренним состоянием (переменная threshold), и для инкапсуляции этих данных логично воспользоваться именно классом. WidgetNameCompare состояния не имеет, поэтому и закрытые данные не нужны. Авторы классов функторов, в которых вся информация является открытой, часто объявляют структуры вместо классов - вероятно, только для того, чтобы им не приходилось вводить public перед базовым классом и функцией operatorO. Выбор между классом и структурой при объявлении таких функторов определяется исключительно стилем программирования. Если вы еще не выработали собственного стиля и стараетесь имитировать профессионалов, учтите, что классы функторов без состояния в самой библиотеке STL (например, less<T>, plus<T> и т. д.) обычно записываются в виде структур. Вернемся к определению WidgetNameCompare: struct WidgetNameCompare: std::bi na ry functi on<Widget,Wi dget,bool >{ bool OperatorO(const WidgetS Ihs,const WidgetS rhs) const; Хотя аргументы operatorO относятся к типу const WidgetS, шаблону binary function передается тип Widget. Обычно при передаче unary f uncti on или binary function типов, не являющихся указателями, ключевые слова const и знаки ссылки удаляются... только не спрашивайте, почему, - ответ на этот вопрос не интересен и не принципиален. Если вы сгораете от любопытства, напишите программу, в которой они не удаляются, и проанализируйте полученную диагностику компилятора. А если вы и после этого не утратите интерес к этой теме, посетите сайт boost.org (см. совет 50) и поищите на нем информацию об адаптерах объектов функций. Если operatorO получает параметры-указатели, ситуация меняется. Ниже приведена структура, аналогичная WidgetNameCompare, но работающая с указателями Widget*: struct PtrWidgetNameCompare: std:;binary function<const Widget*, const Widget*.bool>{ bool operatorO(const Widget* Ihs. const Widget* rhs) const: В этом случае типы, передаваемые binary finotion, совпадают с типами, передаваемыми operatorO. Общее правило для классов функторов, получающих или возвращающих указатели, заключается в том, что unary function или binary function передаются в точности те типы, которые получает или возвращает operatorO. Помните, что базовые классы unary function и binary function выполняют только одну важную функцию - они предоставляют определения типов, необходимые для работы адаптеров, поэтому наследование от этих классов порождает адаптируемые объекты функций. Это позволяет использовать в программах следующие конструкции: list<Widget> widgets: list<Widget>::reverse iterator il= Найти последний объект find if(widgets.rbegin().widgets.rendO, Widget, не соответствующий notl(MeetsThreshold<int>(10))): пороговому критерию 10 (что бы это ни означало) Widget 1{аргументы конструктора): II Найти первый объект Widget, list<Widget>::iterator 12 = предшествующий w в порядке find if(widgets.begin(),widgets.endO, сортировки, определенном bing2nd(WidgetNameCompare(),w)): WidgetNameCompare Если бы классы функторов не определялись производными от unary function или binary function, ни один из этих примеров не компилировался бы, поскольку notl и bind2nd работают только с адаптируемыми объектами функций. Объекты функций STL построены по образцу функций С++, а функции С++ характеризуются единственным набором типов параметров и одним типом возвращаемого значения. В результате STL неявно подразумевает, что каждый класс функтора содержит единственную функцию operatorO, типы параметров и возвращаемого значения которой должны передаваться unary function или binary function (с учетом правил передачи ссылок и указателей, о которых говорилось ранее). Из этого следует одно важное обстоятельство: не поддавайтесь соблазну и не пытайтесь объединять функциональность WidgetnNameCompare и PtrWidgetCompare в одной структуре с двумя функциями operatorO. В этом случае функтор будет адаптируемым по отношению лишь к одной из двух форм вызова (той, что использовалась при передаче параметров binary function), а пользы от такого решения будет немного - наполовину адаптируемый функтор ничуть не лучше неадаптируемого. Иногда в классе функтора бывает разумно определить несколько форм вызова, тем самым отказавшись от адаптируемости (примеры таких ситуаций приведены в советах 7, 20, 23 и 25), но это скорее исключение, а не правило. Адаптируемость важна, и о ней следует помнить при разработке классов функторов. Совет 41. Разберитесь, для чего нужны ptr fun, mem fun и mem fun ref Загадочные функции ptr f un/mein f un/mein f unref часто вызывают недоумение. В одних случаях их присутствие обязательно, в других они не нужны... но что же они все-таки делают? На первый взгляд кажется, что они бессмысленно загромождают имена функций. Их неудобно вводить и читать, они затрудняют понимание программы. Что это - очередные пережитки прошлого STL (другие примеры приводились в советах 10 и 18) или синтаксическая шутка, придуманная членами Комитета по стандартизации с извращенным чувством юмора? Действительно, имена выглядят довольно странно, но функции ptr fun, iiiein fun и mein fun ref выполняют важные задачи. Если уж речь зашла о синтаксических странностях, надо сказать, что одна из важнейших задач этих функций связана с преодолением синтаксической непоследовательности C-I-+. В С++ существуют три варианта синтаксиса вызова функции f для объекта х: f(x): Синтаксис 1: f не является функцией класса (вызов внешней функции) x.fO: Синтаксис 2; f является функцией класса, а X является объектом или ссылкой на объект p->f(): Синтаксис 3: f является функцией класса, а р содержит указатель на х Рассмотрим гипотетическую функцию, предназначенную для проверки объектов Widget: void test(Widget& w): Проверить объект w. Если объект не проходит проверку, он помечается как плохой Допустим, у нас имеется контейнер объектов Widget: vector<Widget> vw; vw содержит объекты Widget Для проверки всех объектов Widget в контейнере vw можно воспользоваться алгоритмом for each: for each(vw.begin().vw.endO.test): Вариант 1 (нормально компилируется) Но представьте, что test является функцией класса Widget, а не внешней функцией (то есть класс Widget сам обеспечивает проверку своих объектов): class Widget { public: void testO: Выполнить самопроверку. Если проверка завершается неудачей, объект помечается }: как плохой В идеальном мире мы могли бы воспользоваться for each для вызова функции Widget: :test всех объектов вектора vw: for each(vw.begin(),vw.endO, &Widget::test): Вариант 2 (не компилируется!)
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.002
При копировании материалов приветствуются ссылки. |