Д. Стефенс - C++. Сборник рецептов
- Название:C++. Сборник рецептов
- Автор:
- Жанр:
- Издательство:КУДИЦ-ПРЕСС
- Год:2007
- Город:Москва
- ISBN:5-91136-030-6
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Д. Стефенс - C++. Сборник рецептов краткое содержание
Данная книга написана экспертами по C++ и содержит готовые рецепты решения каждодневных задач для программистов на С++. Один из авторов является создателем библиотеки Boost Iostreams и нескольких других библиотек C++ с открытым исходным кодом. В книге затрагивается множество тем, вот лишь некоторые из них: работа с датой и временем; потоковый ввод/вывод; обработка исключений; работа с классами и объектами; сборка приложений; синтаксический анализ XML-документов; программирование математических задач. Читатель сможет использовать готовые решения, а сэкономленное время и усилия направить на решение конкретных задач.
C++. Сборник рецептов - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Несмотря на мою привычку тщательно подходить к кодированию программ, я сделал глупую ошибку и написал +=вместо +. В результате при вызове concatUnsafeбудут модифицированы аргументы outи s1, что может оказаться сюрпризом для пользователя, который едва ли рассчитывает на модификацию одной из исходных строк.
Спасти может const. Создайте новую функцию concatSafe, объявите переменные константными, как показано в примере 15.3, и функция не будет откомпилирована.
void concatSafe(const std::string& s1,
const std::string& s2, std::string& out) {
out = s1 += s2; // Теперь вы получите ошибку компиляции
}
concatSafегарантирует неизменяемость значений в s1и s2. Эта функция делает еще кое-что: она позволяет пользователю передавать константные аргументы. Например, программный код, выполняющий конкатенацию строк, мог бы выглядеть следующим образом.
void myFunc(const std::string& s) { // Обратите внимание, что s является
// константной переменной
std::string dest;
std::string tmp = "foo";
concatUnsafe(s, tmp, dest); // Ошибка: s - константная переменная
// Выполнить какие-то действия с dest...
}
В данном случае функция myFuncне будет откомпилирована, потому что concatUnsafeне обеспечивает const'антность myFunc. myFuncгарантирует внешнему миру, что она не будет модифицировать содержимое s, т.е. все действия с sвнутри тела myFuncне должны нарушать это обещание. Конечно, вы можете обойти это ограничение, используя оператор const_castи тем самым освобождаясь от константности, но такой подход ненадежен, и его следует избегать. В этой ситуации concatSafeбудет компилироваться и выполняться нормально.
Указатели вносят темные штрихи в розовую картину const. Когда вы объявляете переменную-указатель как параметр, вы имеет дело с двумя объектами: самим адресом и то, на что ссылается этот адрес. C++ позволяет использовать constдля ограничения действий по отношению к обоим объектам. Рассмотрим еще одну функцию конкатенации, которая использует указатели.
void concatUnsafePtr(std::string* ps1,
std::string* ps2, std::string* pout) {
*pout = *ps1 + *ps2;
}
Здесь такая же проблема, как в примере с concatUnsafe, описанном ранее. Добавьте constдля гарантии невозможности обновления исходных строк.
void concatSaferPtr(const std::string* ps1,
const std::string* ps2, std::string* pout) {
*pout = *ps1 + *ps2;
}
Отлично, теперь вы не можете изменить *ps1и *ps2. Но вы по-прежнему можете изменить ps1и ps2, или, другими словами, используя их, вы можете сослаться на какую-нибудь другую строку, изменяя значение указателя, но не значение, на которое он ссылается. Ничто не может помешать вам, например, сделать следующее.
void concatSaferPtr(const std:string* ps1,
const std::string* ps2, std::string* pout) {
ps1 = pout; // Ух!
*pout = *ps1 + *ps2;
}
Предотвратить подобные ошибки можно с помощью еще одного const.
void concatSafestPtr(const std::string* const ps1,
const std::string* const ps2, std::string* pout) {
*pout = *ps1 + *ps2;
}
Применение constпо обе стороны звездочки делает вашу функцию максимально надежной. В этом случае вы ясно показываете свои намерения пользователям вашей функции, и ваша репутация не пострадает в случае описки.
Рецепт 15.4.
15.4. Обеспечение невозможности модификации своих объектов в функции-члене
Требуется вызывать функции -члены для константного объекта, но ваш компилятор жалуется на то, что он не может преобразовать тип используемого вами объекта из константного в неконстантный.
Поместите ключевое слово constсправа от имени функции-члена при ее объявлении в классе и при ее определении. Пример 15.4 показывает, как это можно сделать
Пример 15.4. Объявление функции-члена константной
#include
#include
class RecordSet {
public:
bool getFieldVal(int i, std::string& s) const;
// ...
};
bool RecordSet::getFieldVal(int i, std::string& s) const {
// Здесь нельзя модифицировать никакие неизменяемые
// данные-члены (см. обсуждение)
}
void displayRecords(const RecordSet& rs) {
// Здесь вы можете вызывать только константные функции-члены
// для rs
}
Добавление концевого constв объявление члена и в его определение заставляет компилятор более внимательно отнестись к тому, что делается с объектом внутри тела члена. Константным функциям-членам не разрешается выполнять неконстантные операции с данными-членами. Если такие операции присутствуют, компиляция завершится неудачно. Например, если бы в RecordSet::getFieldValя обновил счетчик-член, эта функция не была бы откомпилирована (в предположении, что getFieldCount_является переменной-членом класса RecordSet).
bool RecordSet::getFieldVal(int i, std::string& s) const {
++getFieldCount_; // Ошибка: константная функция-член не может
// модифицировать переменную-член
// ...
}
Это может также помочь обнаружить более тонкие ошибки, подобно тому, что делает constв роли квалификатора переменной (см. рецепт 15.3). Рассмотрим следующую глупую ошибку.
bool RecordSet::getFieldVal(int i, std::string& s) const {
fieldArray_[i] = s; // Ой, я не это имел в виду
// ...
}
Снова компилятор преждевременно завершит работу и выдаст сообщение об ошибке, потому что вы пытаетесь изменить переменную-член, а это не разрешается делать в константных функциях-членах. Ну, при одном исключении.
В классе RecordSet(в таком, как (схематичный) класс в примере 15.4) вам, вероятно, потребовалось бы перемещаться туда-сюда по набору записей, используя понятие «текущей» записи. Простой способ заключается в применении переменной-члена целого типа, содержащей номер текущей записи; ваши функции-члены, предназначенные для перемещения текущей записи вперед-назад, должны увеличивать или уменьшать это значение.
void RecordSet::gotoNextPecord() const {
if (curIndex_ >= 0 && curIndex_ < numRecords_-1)
++curIndex_;
}
void RecordSet::gotoPrevRecord() const {
if (curIndex_ > 0)
--curIndex_;
}
Очевидно, что это не сработает, если эти функции-члены являются константными. Обе обновляют данное-член. Однако без этого пользователи класса RecordSetне смогут перемещаться по объекту const RecordSet. Это исключение из правил работы с константными функциями-членами является вполне разумным, поэтому C++ имеет механизм его поддержки: ключевое слово mutable.
Для того чтобы curIndex_можно было обновлять в константной функции-члене, объявите ее с ключевым словом mutable в объявлении класса.
Интервал:
Закладка: