C++. Сборник рецептов - [202]

Шрифт
Интервал

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

>vector 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

>#include


>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_ не является каким-то конкретным значением, содержащимся в памяти; оно ссылается на имя класса, а не на имя объекта, но тогда что же это такое на самом деле? Можно представить это значение как смешение данного-члена относительно начального адреса объекта.

Переменная >mpi должна использоваться совместно с экземпляром класса, к которому она применяется. Немного ниже в примере 15.2 располагается следующая строка, которая использует >mpi для присваивания целого числа значению, на которое ссылается указатель >mpi.

>obj.*mpi = 5;

>obj является экземпляром класса >MyClass. Ссылка на член с использованием точки (или >->, если у вас имеется указатель на >obj) и разыменование >mpi позволяют вам получить ссылку на >obj.ival_.

Указатели на функции-члены действуют фактически так же. В примере 15.2 объявляется указатель на функцию-член >MyClass, которая возвращает >void и не имеет аргументов.

>void (MyClass::*mpf)();

Ему можно присвоить значение с помощью оператора адресации.

>mpf = &MyClass::incr;

Для вызова функции заключите основное выражение в скобки, чтобы компилятор понял ваши намерения, например:

>(obj.*mpf)();

Однако имеется одно отличие в применении указателей на данные-члены и указателей на функции члены. Если необходимо использовать обычный указатель (не на член класса) на данное-член, просто действуйте обычным образом.

>int* pi = &obj.ival_;

Конечно, вы используете имя объекта, а не имя класса, потому что получаете адрес конкретного данного-члена конкретного объекта, расположенного где-то в памяти. (Однако обычно стараются адреса данных-членов класса не выдавать за его пределы, чтобы нельзя было их изменить из-за опрометчивых действий в клиентском программном коде.)


Рекомендуем почитать
Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.


Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.


MFC и OpenGL

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


Симуляция частичной специализации

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


Обработка событий в С++

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


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.