Д. Стефенс - C++. Сборник рецептов
- Название:C++. Сборник рецептов
- Автор:
- Жанр:
- Издательство:КУДИЦ-ПРЕСС
- Год:2007
- Город:Москва
- ISBN:5-91136-030-6
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Д. Стефенс - C++. Сборник рецептов краткое содержание
Данная книга написана экспертами по C++ и содержит готовые рецепты решения каждодневных задач для программистов на С++. Один из авторов является создателем библиотеки Boost Iostreams и нескольких других библиотек C++ с открытым исходным кодом. В книге затрагивается множество тем, вот лишь некоторые из них: работа с датой и временем; потоковый ввод/вывод; обработка исключений; работа с классами и объектами; сборка приложений; синтаксический анализ XML-документов; программирование математических задач. Читатель сможет использовать готовые решения, а сэкономленное время и усилия направить на решение конкретных задач.
C++. Сборник рецептов - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Используйте имя класса и оператор области видимости ( ::
) со звездочкой для правильного квалифицирования имени. Пример 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_;
Конечно, вы используете имя объекта, а не имя класса, потому что получаете адрес конкретного данного-члена конкретного объекта, расположенного где-то в памяти. (Однако обычно стараются адреса данных-членов класса не выдавать за его пределы, чтобы нельзя было их изменить из-за опрометчивых действий в клиентском программном коде.)
В отличие от данного члена с функцией-членом вы не можете сделать то же самое, потому что это бессмысленно. Рассмотрим указатель на функцию, имеющую такую же сигнатуру, как MyClass::incr
(т.е. он возвращает void
и не имеет аргументов).
void (*pf)();
Теперь попытайтесь присвоить этому указателю адрес функции-члена.
pf = &MyClass::incr; // He получится
pf = &obj.incr; // И это не пройдет
Обе эти строки не будут откомпилированы, и на это имеются веские основания. Применение функции-члена имеет разумный смысл только в контексте объекта, поскольку, вероятнее всего, она должна ссылаться на переменные-члены. Вызов функции-члена без объекта означало бы невозможность в функции-члене использовать какие-либо члены объекта, а эта функция, по-видимому, как раз является функцией-членом, а не автономной функцией, потому что использует члены объекта.
Рецепт 15.1.
15.3. Обеспечение невозможности модификации аргумента в функции
Вы пишете функцию и требуется гарантировать, что ее аргументы не будут модифицированы при ее вызове.
Для предотвращения изменения аргументов вашей функцией объявите ее аргументы с ключевым словом const
. Короткий пример 15.3 показывает, как это можно сделать.
Пример 15.3. Гарантия невозможности модификации аргументов
#include
#include
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
}
Интервал:
Закладка: