Чтение онлайн

ЖАНРЫ

C++. Сборник рецептов

Когсуэлл Джефф

Шрифт:

 for (long l=0; l < 100000000; l++)

 if (l % 10000000 == 0)

 f(l/1000000);

}

int main {

 longOperation(updateProgress); // нормально

}

Обсуждение

В ситуации, которая показана в примере 15.1, применение указателя на функцию является хорошим решением, если

UpdateProgress
и
longOperation
ничего не должны знать друг о друге. Например, функцию, которая обновляет индикатор
состояния процесса в диалоговом окне пользовательского интерфейса (user interface — UI), в окне консольного режима или где-то еще, не заботит контекст, в котором она вызывается. Аналогично функция
longOperation
может быть частью некоторого программного интерфейса загрузки данных, которого не заботит место вызова: из графического UI, из окна консольного режима или из фонового процесса.

Сначала потребуется определить сигнатуру функции, которую вы планируете вызывать, и создать для нее

typedef
. Оператор
typedef
— ваш помощник в тех случаях, когда приходится иметь дело с указателями функций, потому что они имеют не очень привлекательный синтаксис. Рассмотрим, как обычно объявляется такой указатель на примере переменной
f
, которая содержит адрес функции, принимающей единственный аргумент целого типа и возвращающей значения типа
boolean
. Это может выглядеть следующим образом

bool (*f)(int); // f - имя переменной

Вы можете справедливо возразить, что здесь нет ничего особенного и я просто излишне драматизирую ситуацию. Но что вы скажете, если требуется определить вектор

vector
таких указателей?

vector<bool (*)(int)> vf;

Или их массив?

bool (*af[10])(int);

Форма представления указателей на функции отличается от обычных переменных С++, которые обычно задаются в виде (квалифицированного) имени типа, за которым идет имя переменной. Поэтому они вносят путаницу при чтении программного кода.

Итак, в примере 15.1 я использовал следующий

typedef
.

typedef bool (*FuncPtrBoolInt)(int);

Сделав это, я могу свободно объявлять указатели функций с сигнатурой, возвращающей значение

bool
и принимающей единственный аргумент, как это я бы делал для параметра любого другого типа, например.

void longOperation(FuncPtrBoolInt f) { // ...

Теперь все, что надо сделать в

longOperation
, — это вызвать
f
, как если бы это была любая обычная функция.

f(l/1000000);

Таким образам, здесь

f
может быть любой функцией, которая принимает аргумент целого типа и возвращает
bool
. Предположим, что в вызывающей функции
longOperation
не требуется обеспечивать продвижение индикатора состояния процесса. Тогда ей можно передать указатель на функцию без операций.

bool whoCares(int i) {return(true);}

//...

longOperation(whoCares);

Более важно то, что выбор функции, передаваемой

longOperation
, может осуществляться динамически на этапе выполнения.

15.2. Применение указателей для членов класса

Проблема

Требуется обеспечить адресную ссылку на данное-член или на функцию-член.

Решение

Используйте имя класса и оператор области видимости (

::
) со звездочкой для правильного квалифицирования имени. Пример 15.2 показывает, как это можно сделать.

Пример 15.2.

Получение указателя на член класса

#include <iostream>

#include <string>

class MyClass {

public:

 MyClass : ival_(0), sval_("foo") {}

 ~MyClass {}

 void incr {++ival_;}

 void decr {ival_--;}

private:

 std::string sval_;

 int ival_;

};

int main {

 MyClass obj;

 int MyClass::* mpi = &MyClass::ival_; // Указатели на

 std::string MyClass::* mps = &MyClass::sval_; // данные-члены

 void (MyClass::*mpf); // Указатель на функцию-член, у которой

// нет параметров и которая возвращает void

 void (*pf); // Обычный указатель на функцию

 int* pi = &obj.ival_; // int-указатель, ссылающийся на переменную-член

// типа int, - все нормально.

 mpf = &MyClass::incr; // Указатель на функцию-член. Вы не можете

// записать это значение в поток. Посмотрите в

// отладчике, как это значение выглядит.

 pf = &MyClass::incr; // Ошибка: &MyClass::inc не является экземпляром

// функции

 std::cout << "mpi = " << mpi << '\n';

 std::cout << "mps = " << mps << '\n';

 std::cout << "pi = " << pi << '\n';

 std::cout << "*pi = " << *pi << '\n';

 obj.*mpi = 5;

 obj.*mps = "bar";

 (obj.*mpf); // теперь obj.ival_ равно 6

 std::cout << "obj.ival_ = " << obj.ival_ << '\n';

 std::cout << "obj.sval_ = " << obj.sval_ << '\n';

}

Обсуждение

Указатели на члены класса выглядят и работают иначе, чем обычные указатели. Прежде всего, они имеют «смешной» синтаксис (не вызывающий смех, но странный). Рассмотрим следующую строку из примера 15.2.

int MyClass::* mpi = &MyClass::ival_;

Здесь объявляется указатель и ему присваивается значение целого типа, которым оказывается член класса

MyClass
. Две вещи отнимают это объявление от обычного
int*
. Во-первых, вам приходится вставлять имя класса и оператор области видимости между типом данного и звездочкой. Во-вторых, при выполнении операции присваивания этому указателю на самом деле не назначается какой то определенный адрес памяти. Значение
&MyClass::ival_
не является каким-то конкретным значением, содержащимся в памяти; оно ссылается на имя класса, а не на имя объекта, но тогда что же это такое на самом деле? Можно представить это значение как смешение данного-члена относительно начального адреса объекта.

Поделиться с друзьями: