Анимация
JavaScript


Главная  Библионтека 

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

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