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

на главную - закладки

Жанры

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

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

Шрифт:

 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_;

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

В отличие от данного члена с функцией-членом вы не можете сделать то же самое, потому что это бессмысленно. Рассмотрим указатель на функцию, имеющую такую же сигнатуру, как

MyClass::incr
(т.е. он возвращает
void
и не имеет аргументов).

void (*pf);

Теперь попытайтесь присвоить этому указателю адрес функции-члена.

pf = &MyClass::incr; // He получится

pf = &obj.incr; // И это не пройдет

Обе

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

См. также

Рецепт 15.1.

15.3. Обеспечение невозможности модификации аргумента в функции

Проблема

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

Решение

Для предотвращения изменения аргументов вашей функцией объявите ее аргументы с ключевым словом

const
. Короткий пример 15.3 показывает, как это можно сделать.

Пример 15.3. Гарантия невозможности модификации аргументов

#include <iostream

#include <string>

void concat(const std::string& s1, // Аргументы объявлены как константное,

 const std::string& s2, // поэтому не могут быть изменены

 std::string& out) {

 out = s1 + s2;

}

int main {

 std::string s1 = "Cabo ";

 std::string s2 = "Wabo";

 std::string s3;

 concat(s1, s2, s3);

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

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

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

}

Обсуждение

В примере 15.3 продемонстрировано прямое использование ключевого слова

const
. Существует две причины объявления параметров вашей функции с этим ключевым словом, когда вы не планируете их изменять. Во-первых, этим вы сообщаете о своих намерениях читателям вашего программного кода. Объявляя параметр как
const
, вы фактически говорите, что он является входным параметром. Это позволяет пользователям вашей функции писать программный код в расчете на то, что эти значения не будут изменены. Во-вторых, это позволяет компилятору запретить любые модифицирующие операции на тот случай, если вы случайно их используете. Рассмотрим небезопасную версию
concat
из примера 15 3.

void concatUnsafe(std::string& s1,

 std::string& s2 std::string& out) {

 out = s1 += s2; // Ну вот, записано значение в s1

}

Несмотря на мою привычку тщательно подходить к кодированию программ, я сделал глупую ошибку и написал

+=
вместо
+
. В результате при вызове
concatUnsafe
будут модифицированы аргументы
out
и
s1
, что может оказаться сюрпризом для пользователя, который едва ли рассчитывает на модификацию одной из исходных строк.

Поделиться:
Популярные книги

Как я строил магическую империю 3

Зубов Константин
3. Как я строил магическую империю
Фантастика:
попаданцы
постапокалипсис
аниме
фэнтези
5.00
рейтинг книги
Как я строил магическую империю 3

Ритуал для призыва профессора

Лунёва Мария
Любовные романы:
любовно-фантастические романы
7.00
рейтинг книги
Ритуал для призыва профессора

Невеста снежного демона

Ардова Алиса
Зимний бал в академии
Фантастика:
фэнтези
6.80
рейтинг книги
Невеста снежного демона

Прорвемся, опера! Книга 3

Киров Никита
3. Опер
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Прорвемся, опера! Книга 3

Леди Малиновой пустоши

Шах Ольга
Любовные романы:
любовно-фантастические романы
6.20
рейтинг книги
Леди Малиновой пустоши

Мымра!

Фад Диана
1. Мымрики
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Мымра!

Матабар

Клеванский Кирилл Сергеевич
1. Матабар
Фантастика:
фэнтези
5.00
рейтинг книги
Матабар

Офицер Красной Армии

Поселягин Владимир Геннадьевич
2. Командир Красной Армии
Фантастика:
попаданцы
8.51
рейтинг книги
Офицер Красной Армии

Волхв пятого разряда

Дроздов Анатолий Федорович
2. Ледащий
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Волхв пятого разряда

Вамп

Парсиев Дмитрий
3. История одного эволюционера
Фантастика:
рпг
городское фэнтези
постапокалипсис
5.00
рейтинг книги
Вамп

Младший сын князя. Том 3

Ткачев Андрей Юрьевич
3. Аналитик
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Младший сын князя. Том 3

Шахта Шепчущих Глубин, Том II

Астахов Евгений Евгеньевич
3. Виашерон
Фантастика:
фэнтези
7.19
рейтинг книги
Шахта Шепчущих Глубин, Том II

Наследник пепла. Книга I

Дубов Дмитрий
1. Пламя и месть
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Наследник пепла. Книга I

Газлайтер. Том 9

Володин Григорий
9. История Телепата
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Газлайтер. Том 9