Программирование >>  Включение нужных заголовков 

1 ... 46 47 48 [ 49 ] 50 51 52 ... 71


Пара примеров:

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 (не компилируется!)



1 ... 46 47 48 [ 49 ] 50 51 52 ... 71

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.002
При копировании материалов приветствуются ссылки.
Яндекс.Метрика