Анимация
JavaScript
|
Главная Библионтека class Fn { private: int number; public: f(int n) : number(n) {} void operator() () { do something(number); } void callbackfn(Fn); void p(int n) ca11backfn(Fn(n)); void ca11backfn(Fn fn) Что-то делаем fn(); Вызвать «функцию» fn с помощью функции operator() Весь секрет кроется в двух выражениях. Функция ca11backfn(Fn(n)) передает функции анонимный экземпляр класса Fn. Аргумент его конструктора содержит информацию, включаемую в «псевдозамыкание», которое поддерживается переменными класса Fn. Выражение fn(); может показаться обычным вызовом функции, но на самом деле в нем вызывается операторная функция operator() класса Fn. В свою очередь, эта функция вызывает глобальную функцию do something с использованием данных замыкания. И кому после этого нужен Паскаль? Операторная функция operator() может вызываться с произвольным набором аргументов. Чтобы добавить новые аргументы, укажите их во вторых скобках в объявлении класса. Также разрешается многократная перегрузка оператора () с разными сигнатурами. Ниже приведен тот же пример, в котором одна из версий операторной функции operator() вызывается с аргументом. class Fn { private: int number; public: f(int n) : number(n) {} void operator() () { do something(number); } void operator() (char* s) do something(number); cout << "Что-то делаю с " << s << endl; void callbackfn(Fn); void p(int n) ca11backfn(Fn(n)); void ca11backfn(Fn fn) Что-то делаем fn("ca11backfn"); Эта маленькая идиома выглядит довольно изящно, однако того же эффекта можно добиться и без оператора () . class Fn { private: int number; public: f(int n) : number(n) {} void do something() () { ::do something(number); } void do something() (char* s) do something(number); cout << "Что-то делаю с " << s << endl; void callbackfn(Fn); void p(int n) ca11backfn(Fn(n)); void ca11backfn(Fn fn) Что-то делаем fn.do something("ca1Ibackfn"); Как видите, с таким же успехом можно воспользоваться именем любой функции класса. Единственная причина для использования оператора () - в том, что он предельно ясно выражает ваши намерения. Если класс существует лишь для того, чтобы обслуживать обратные вызовы подобного рода, пользуйтесь оператором () ; в противном случае пользуйтесь обычными функциями класса. Коллекции, курсоры и итераторы Проблема проектирования и реализации классов-коллекций (совокупностей объектов, находящихся под управлением другого объекта) стара, как само объектно-ориентированное программирование. Нет смысла повторять здесь все, что можно прочитать в других книгах о всевозможных коллекциях - изменяемых, сериализуемых, индексируемых и т. д. Если вам нужна информация о структурах данных и сопутствующих трансформациях, начните с изучения классов-коллекций SmallTalk и затем переходите к коммерческим библиотекам классов C++, рекламой которых забиты все журналы по программному обеспечению. Я же собираюсь сосредоточить ваше внимание на тех С++-измах, благодаря которым коллекции укладываются в мистическое учение программирования на C++ независимо от используемых структур данных и иерархий классов. В начале этой главы рассматриваются индексируемые коллекции - то есть те, в которых некоторый объект используется в качестве индекса для получения другого объекта, скрытого глубоко в недрах коллекции. Мы воспользуемся оператором [], чтобы коллекция выглядела как абстрактный массив. Дело даже не в том, что программа от этого обычно становится более понятной - в этом подходе задействованы многие идиомы, используемые для коллекций. После знакомства с курсорами и их собратьями итераторами мы перейдем к изощренным коллекциям, модифицируемым в процессе перебора. Массивы и оператор [] Оператор [] чрезвычайно гибок и универсален. К сожалению, большинство программистов C++ об этом и не подозревает. Проверка границ и присваивание Ниже приведен простой пример перегрузки оператора [] - массив, который при обращении к элементу проверяет, принадлежит ли индекс границам массива, и в любых ситуациях ведет себя более или менее разумно (или по крайней мере безопасно). class ArrayOfFoo { private: int entries; Foo** contents; Вектор Foo* static Foo* dummy; Для индексов, выходящих за границы массива public: ArrayOfFoo() : entries(O), contents(NULL) {}; ArrayOfFoo(int size) : entries(size), contents(new Foo*[size]) {}; ~ArrayOfFoo() { delete contents; } Foo*& operator[](int index) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 [ 32 ] 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |